summaryrefslogtreecommitdiff
path: root/dryad/user.go
diff options
context:
space:
mode:
authorAleksander Mistewicz <a.mistewicz@samsung.com>2017-09-08 14:52:34 +0200
committerMaciej Wereski <m.wereski@partner.samsung.com>2018-03-05 17:56:44 +0100
commite706b448db0c82642d773e7c13ff46c1ec859618 (patch)
tree519fab048dbf1c87d5f3ee3ea952e9f6db3635bb /dryad/user.go
parentaa9ba4874e92fae53fd22f064d3c485af2fadaba (diff)
downloadboruta-e706b448db0c82642d773e7c13ff46c1ec859618.tar.gz
boruta-e706b448db0c82642d773e7c13ff46c1ec859618.tar.bz2
boruta-e706b448db0c82642d773e7c13ff46c1ec859618.zip
Add dryad package
Package dryad provides: * implementation of Dryad interface * utilities to manage Dryad and its users Verification steps: // compile tests GOARCH=arm GOARM=7 GOOS=linux ginkgo build -cover dryad // copy dryad/dryad.test to device // run everything ./dryad.test -ginkgo.v // run without measurements ./dryad.test -ginkgo.v -ginkgo.skipMeasurements // run prepare only ./dyrad.test -ginkgo.focus "should prepare" // private key will be printed on stderr // it can be used to verify that SSH key is properly installed Currently dryad is being run as root on muxpi. Future patches will probably use sudo and require configured file in /etc/sudoers.d/ directory. Change-Id: I095361ffd1f4b2b3fa5dfe2c000f960dd32886e2 Signed-off-by: Aleksander Mistewicz <a.mistewicz@samsung.com>
Diffstat (limited to 'dryad/user.go')
-rw-r--r--dryad/user.go143
1 files changed, 143 insertions, 0 deletions
diff --git a/dryad/user.go b/dryad/user.go
new file mode 100644
index 0000000..dc2f48e
--- /dev/null
+++ b/dryad/user.go
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017-2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package dryad
+
+import (
+ "crypto/rsa"
+ "fmt"
+ "os/exec"
+ "os/user"
+ "strings"
+ "syscall"
+)
+
+var (
+ userAddExit = map[int]string{
+ 0: "success",
+ 1: "can't update password file",
+ 2: "invalid command syntax",
+ 3: "invalid argument to option",
+ 4: "UID already in use (and no -o)",
+ 6: "specified group doesn't exist",
+ 9: "username already in use",
+ 10: "can't update group file",
+ 12: "can't create home directory",
+ 14: "can't update SELinux user mapping",
+ }
+ userDelExit = map[int]string{
+ 0: "success",
+ 1: "can't update password file",
+ 2: "invalid command syntax",
+ 6: "specified user doesn't exist",
+ 8: "user currently logged in",
+ 10: "can't update group file",
+ 12: "can't remove home directory",
+ }
+)
+
+// borutaUser is a representation of the local Unix user on Dryad.
+type borutaUser struct {
+ username string
+ groups []string
+ user *user.User
+}
+
+func newBorutaUser(username string, groups []string) *borutaUser {
+ return &borutaUser{
+ username: username,
+ groups: groups,
+ }
+}
+
+func prepareGroups(groups []string) string {
+ return strings.Join(groups, ",")
+}
+
+// handleUserCmdError attempts to use predefined error message. Otherwise err and output are used.
+// It works for useradd and userdel exit codes.
+func handleUserCmdError(codeToMsg map[int]string, output []byte, err error) error {
+ // On Unix systems check exit code and replace error message with predefined string.
+ if exiterr, ok := err.(*exec.ExitError); ok {
+ if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
+ if errmsg, ok := codeToMsg[status.ExitStatus()]; ok {
+ return fmt.Errorf("command failed: %s", errmsg)
+ }
+ }
+ }
+ return fmt.Errorf("command failed: %s, %s", err, string(output))
+}
+
+// prepareUserAddCmd is a helper function for add().
+func prepareUserAddCmd(username string, groups []string) *exec.Cmd {
+ // "-p" encrypted password of the new account.
+ // "-m" create the user's home directory.
+ // "-G" list of supplementary groups of the new account.
+ return exec.Command("useradd", "-p", "*", "-m", "-G", prepareGroups(groups), username)
+}
+
+// add creates borutaUser on the system.
+// It does nothing if the user with given username already exists.
+func (bu *borutaUser) add() error {
+ if bu.update() == nil {
+ // user already exists.
+ return nil
+ }
+ output, err := prepareUserAddCmd(bu.username, bu.groups).CombinedOutput()
+ if err != nil {
+ return handleUserCmdError(userAddExit, output, err)
+ }
+ return nil
+}
+
+// prepareUserDelCmd is a helper function for delete().
+func prepareUserDelCmd(username string) *exec.Cmd {
+ // "-r" remove home directory and mail spool.
+ // "-f" force removal of files, even if not owned by user.
+ return exec.Command("userdel", "-r", "-f", username)
+}
+
+// delete removes borutaUser from the system. It does nothing if the user with given username
+// has already been removed. It invalidates user field of borutaUser.
+func (bu *borutaUser) delete() error {
+ err := bu.update()
+ if err != nil {
+ if _, ok := err.(user.UnknownUserError); ok {
+ // user already does not exist.
+ return nil
+ }
+ return err
+ }
+ output, err := prepareUserDelCmd(bu.username).CombinedOutput()
+ if err != nil {
+ return handleUserCmdError(userDelExit, output, err)
+ }
+ bu.user = nil
+ return nil
+}
+
+// update retrieves information about borutaUser from the system. It should be used after userAdd.
+// Stored information may be invalid after call to userdel.
+func (bu *borutaUser) update() (err error) {
+ bu.user, err = user.Lookup(bu.username)
+ return
+}
+
+// generateAndInstallKey calls generateAndInstallKey with parameters retrieved from the user field
+// of borutaUser structure. This filed must be set before call to this function by update() method.
+func (bu *borutaUser) generateAndInstallKey() (*rsa.PrivateKey, error) {
+ return generateAndInstallKey(bu.user.HomeDir, bu.user.Uid, bu.user.Gid)
+}