func (s *snapassertsSuite) TestDeriveSideInfoHappy(c *C) { digest := makeDigest(42) size := uint64(len(fakeSnap(42))) headers := map[string]interface{}{ "snap-id": "snap-id-1", "snap-sha3-384": digest, "snap-size": fmt.Sprintf("%d", size), "snap-revision": "42", "developer-id": s.dev1Acct.AccountID(), "timestamp": time.Now().Format(time.RFC3339), } snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapRev) c.Assert(err, IsNil) tempdir := c.MkDir() snapPath := filepath.Join(tempdir, "anon.snap") err = ioutil.WriteFile(snapPath, fakeSnap(42), 0644) c.Assert(err, IsNil) si, err := snapasserts.DeriveSideInfo(snapPath, s.localDB) c.Assert(err, IsNil) c.Check(si, DeepEquals, &snap.SideInfo{ RealName: "foo", SnapID: "snap-id-1", Revision: snap.R(42), Channel: "", DeveloperID: s.dev1Acct.AccountID(), Developer: "developer1", }) }
func (s *snapassertsSuite) TestDeriveSideInfoNoSignatures(c *C) { tempdir := c.MkDir() snapPath := filepath.Join(tempdir, "anon.snap") err := ioutil.WriteFile(snapPath, fakeSnap(42), 0644) c.Assert(err, IsNil) _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) // cannot find signatures with metadata for snap c.Assert(err, Equals, asserts.ErrNotFound) }
func (s *snapassertsSuite) TestDeriveSideInfoRevokedSnapDecl(c *C) { // revoked snap declaration (snap-name=="") ! headers := map[string]interface{}{ "series": "16", "snap-id": "snap-id-1", "snap-name": "", "publisher-id": s.dev1Acct.AccountID(), "revision": "1", "timestamp": time.Now().Format(time.RFC3339), } snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapDecl) c.Assert(err, IsNil) digest := makeDigest(42) size := uint64(len(fakeSnap(42))) headers = map[string]interface{}{ "snap-id": "snap-id-1", "snap-sha3-384": digest, "snap-size": fmt.Sprintf("%d", size), "snap-revision": "42", "developer-id": s.dev1Acct.AccountID(), "timestamp": time.Now().Format(time.RFC3339), } snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapRev) c.Assert(err, IsNil) tempdir := c.MkDir() snapPath := filepath.Join(tempdir, "anon.snap") err = ioutil.WriteFile(snapPath, fakeSnap(42), 0644) c.Assert(err, IsNil) _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) c.Check(err, ErrorMatches, fmt.Sprintf(`cannot install snap %q with a revoked snap declaration`, snapPath)) }
func (s *snapassertsSuite) TestDeriveSideInfoSizeMismatch(c *C) { digest := makeDigest(42) size := uint64(len(fakeSnap(42))) headers := map[string]interface{}{ "snap-id": "snap-id-1", "snap-sha3-384": digest, "snap-size": fmt.Sprintf("%d", size+5), // broken "snap-revision": "42", "developer-id": s.dev1Acct.AccountID(), "timestamp": time.Now().Format(time.RFC3339), } snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") c.Assert(err, IsNil) err = s.localDB.Add(snapRev) c.Assert(err, IsNil) tempdir := c.MkDir() snapPath := filepath.Join(tempdir, "anon.snap") err = ioutil.WriteFile(snapPath, fakeSnap(42), 0644) c.Assert(err, IsNil) _, err = snapasserts.DeriveSideInfo(snapPath, s.localDB) c.Check(err, ErrorMatches, fmt.Sprintf(`snap %q does not have expected size according to signatures \(broken or tampered\): %d != %d`, snapPath, size, size+5)) }
func populateStateFromSeedImpl(st *state.State) ([]*state.TaskSet, error) { // check that the state is empty var seeded bool err := st.Get("seeded", &seeded) if err != nil && err != state.ErrNoState { return nil, err } if seeded { return nil, fmt.Errorf("cannot populate state: already seeded") } // ack all initial assertions if err := importAssertionsFromSeed(st); err != nil { return nil, err } seed, err := snap.ReadSeedYaml(filepath.Join(dirs.SnapSeedDir, "seed.yaml")) if err != nil { return nil, err } tsAll := []*state.TaskSet{} for i, sn := range seed.Snaps { var flags snapstate.Flags if sn.DevMode { flags.DevMode = true } path := filepath.Join(dirs.SnapSeedDir, "snaps", sn.File) var sideInfo snap.SideInfo if sn.Unasserted { sideInfo.RealName = sn.Name } else { si, err := snapasserts.DeriveSideInfo(path, assertstate.DB(st)) if err == asserts.ErrNotFound { return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path) } if err != nil { return nil, err } sideInfo = *si sideInfo.Private = sn.Private } ts, err := snapstate.InstallPath(st, &sideInfo, path, sn.Channel, flags) if i > 0 { ts.WaitAll(tsAll[i-1]) } if err != nil { return nil, err } tsAll = append(tsAll, ts) } if len(tsAll) == 0 { return nil, nil } ts := tsAll[len(tsAll)-1] markSeeded := st.NewTask("mark-seeded", i18n.G("Mark system seeded")) markSeeded.WaitAll(ts) tsAll = append(tsAll, state.NewTaskSet(markSeeded)) return tsAll, nil }
func populateStateFromSeed() error { if osutil.FileExists(dirs.SnapStateFile) { return fmt.Errorf("cannot create state: state %q already exists", dirs.SnapStateFile) } ovld, err := overlord.New() if err != nil { return err } st := ovld.State() // ack all initial assertions if err := importAssertionsFromSeed(st); err != nil { return err } seed, err := snap.ReadSeedYaml(filepath.Join(dirs.SnapSeedDir, "seed.yaml")) if err != nil { return err } tsAll := []*state.TaskSet{} for i, sn := range seed.Snaps { st.Lock() flags := snapstate.Flags(0) if sn.DevMode { flags |= snapstate.DevMode } path := filepath.Join(dirs.SnapSeedDir, "snaps", sn.File) var sideInfo snap.SideInfo if sn.Unasserted { sideInfo.RealName = sn.Name } else { si, err := snapasserts.DeriveSideInfo(path, assertstate.DB(st)) if err == asserts.ErrNotFound { st.Unlock() return fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path) } if err != nil { st.Unlock() return err } sideInfo = *si sideInfo.Private = sn.Private } ts, err := snapstate.InstallPath(st, &sideInfo, path, sn.Channel, flags) if i > 0 { ts.WaitAll(tsAll[i-1]) } st.Unlock() if err != nil { return err } tsAll = append(tsAll, ts) } if len(tsAll) == 0 { return nil } st.Lock() msg := fmt.Sprintf("First boot seeding") chg := st.NewChange("seed", msg) for _, ts := range tsAll { chg.AddAll(ts) } st.Unlock() // do it and wait for ready ovld.Loop() st.EnsureBefore(0) <-chg.Ready() st.Lock() status := chg.Status() err = chg.Err() st.Unlock() if status != state.DoneStatus { ovld.Stop() return fmt.Errorf("cannot run seed change: %s", err) } return ovld.Stop() }
func postSnaps(c *Command, r *http.Request, user *auth.UserState) Response { contentType := r.Header.Get("Content-Type") if contentType == "application/json" { return snapsOp(c, r, user) } if !strings.HasPrefix(contentType, "multipart/") { return BadRequest("unknown content type: %s", contentType) } route := c.d.router.Get(stateChangeCmd.Path) if route == nil { return InternalError("cannot find route for change") } // POSTs to sideload snaps must be a multipart/form-data file upload. _, params, err := mime.ParseMediaType(contentType) if err != nil { return BadRequest("cannot parse POST body: %v", err) } form, err := multipart.NewReader(r.Body, params["boundary"]).ReadForm(maxReadBuflen) if err != nil { return BadRequest("cannot read POST form: %v", err) } dangerousOK := isTrue(form, "dangerous") devmode := isTrue(form, "devmode") flags, err := modeFlags(devmode, isTrue(form, "jailmode")) if err != nil { return BadRequest(err.Error()) } if len(form.Value["action"]) > 0 && form.Value["action"][0] == "try" { if len(form.Value["snap-path"]) == 0 { return BadRequest("need 'snap-path' value in form") } return trySnap(c, r, user, form.Value["snap-path"][0], flags) } // find the file for the "snap" form field var snapBody multipart.File var origPath string out: for name, fheaders := range form.File { if name != "snap" { continue } for _, fheader := range fheaders { snapBody, err = fheader.Open() origPath = fheader.Filename if err != nil { return BadRequest(`cannot open uploaded "snap" file: %v`, err) } defer snapBody.Close() break out } } defer form.RemoveAll() if snapBody == nil { return BadRequest(`cannot find "snap" file field in provided multipart/form-data payload`) } tmpf, err := ioutil.TempFile("", "snapd-sideload-pkg-") if err != nil { return InternalError("cannot create temporary file: %v", err) } if _, err := io.Copy(tmpf, snapBody); err != nil { os.Remove(tmpf.Name()) return InternalError("cannot copy request into temporary file: %v", err) } tmpf.Sync() tempPath := tmpf.Name() if len(form.Value["snap-path"]) > 0 { origPath = form.Value["snap-path"][0] } st := c.d.overlord.State() st.Lock() defer st.Unlock() var snapName string var sideInfo *snap.SideInfo if !dangerousOK { si, err := snapasserts.DeriveSideInfo(tempPath, assertstate.DB(st)) switch err { case nil: snapName = si.RealName sideInfo = si case asserts.ErrNotFound: // with devmode we try to find assertions but it's ok // if they are not there (implies --dangerous) if !devmode { msg := "cannot find signatures with metadata for snap" if origPath != "" { msg = fmt.Sprintf("%s %q", msg, origPath) } return BadRequest(msg) } // TODO: set a warning if devmode default: return BadRequest(err.Error()) } } if snapName == "" { // potentially dangerous but dangerous or devmode params were set info, err := unsafeReadSnapInfo(tempPath) if err != nil { return InternalError("cannot read snap file: %v", err) } snapName = info.Name() sideInfo = &snap.SideInfo{RealName: snapName} } msg := fmt.Sprintf(i18n.G("Install %q snap from file"), snapName) if origPath != "" { msg = fmt.Sprintf(i18n.G("Install %q snap from file %q"), snapName, origPath) } var userID int if user != nil { userID = user.ID } tsets, err := withEnsureUbuntuCore(st, snapName, userID, func() (*state.TaskSet, error) { return snapstateInstallPath(st, sideInfo, tempPath, "", flags) }, ) if err != nil { return InternalError("cannot install snap file: %v", err) } chg := newChange(st, "install-snap", msg, tsets, []string{snapName}) chg.Set("api-data", map[string]string{"snap-name": snapName}) ensureStateSoon(st) return AsyncResponse(nil, &Meta{Change: chg.ID()}) }