package main import "os" import "fmt" import "flag" import "strconv" import "os/exec" import "os/user" import "path/filepath" import "golang.org/x/crypto/bcrypt" func printErr (format string, values ...any) { fmt.Fprintf(os.Stderr, os.Args[0] + ": " + format, values...) } func serviceUser (service string) string { return "hn-" + service } func tryCommand (cmd *exec.Cmd, failReason string) { output, err := cmd.CombinedOutput() if err != nil { printErr("%s: %s", failReason, string(output)) os.Exit(1) } } func ownOne (path string, uid, gid int) { file, err := os.Stat(path) if err != nil { printErr("could not stat %s: %v", path, err) return } err = os.Chown(path, uid, gid) if err != nil { printErr("could not change ownership of %s: %v", path, err) return } if file.IsDir() { err = os.Chmod(path, 0770) } else { err = os.Chmod(path, 0660) } if err != nil { printErr("could not change mode of %s: %v", path, err) return } } func main () { user, err := user.Current() if err != nil { printErr("could not get username %v", err) os.Exit(1) } // define commands hashCommand := flag.NewFlagSet("hash", flag.ExitOnError) hashCost := hashCommand.Uint("cost", uint(bcrypt.DefaultCost), "Cost of the hash") hashText := hashCommand.String("k", "", "Text content of the key") addUserCommand := flag.NewFlagSet("adduser", flag.ExitOnError) addUserService := addUserCommand.String ("s", "router", "Service to add a user for") delUserCommand := flag.NewFlagSet("deluser", flag.ExitOnError) delUserService := delUserCommand.String ("s", "router", "Service to delete the user for") authCommand := flag.NewFlagSet("auth", flag.ExitOnError) authService := authCommand.String ("s", "router", "Service to authorize the user to access") authUser := authCommand.String ("u", user.Username, "User to be given access") ownCommand := flag.NewFlagSet("own", flag.ExitOnError) ownService := ownCommand.String ("s", "router", "Service to give ownership of the file to") ownFile := ownCommand.String ("f", ".", "File to take ownership of") ownRecursive := ownCommand.Bool ("r", false, "Whether or not to recurse into sub-directories") // execute correct command switch os.Args[1] { case "hash": execHash(int(*hashCost), *hashText) case "adduser": execAdduser(*addUserService) case "deluser": execDeluser(*delUserService) case "auth": execAuth(*authService, *authUser) case "own": execOwn(*ownService, *ownFile, *ownRecursive) } } func execHash (cost int, key string) { if cost < bcrypt.MinCost { printErr("cost is too low, must be at least %v", bcrypt.MinCost) os.Exit(1) } if cost > bcrypt.MaxCost { printErr("cost is too hight, can be at most %v", bcrypt.MaxCost) os.Exit(1) } hash, err := bcrypt.GenerateFromPassword([]byte(key), cost) if err != nil { printErr("could not hash key: %v", err) os.Exit(1) } fmt.Println(string(hash)) } func execAdduser (service string) { fullName := serviceUser(service) // BUSYBOX adduser, err := exec.LookPath("adduser") if err == nil { addgroup, _ := exec.LookPath("addgroup") tryCommand (exec.Command(addgroup, fullName, "-S"), "could not add group") tryCommand (exec.Command(adduser, fullName, "-SHDG", fullName), "could not add user") return } // GNU useradd, err := exec.LookPath("useradd") if err == nil { tryCommand (exec.Command ( useradd, fullName, "-rUM", "--shell", "/sbin/nologin"), "could not add user") return } printErr("could not add user: no command adduser or useradd") os.Exit(1) } func execDeluser (service string) { fullName := serviceUser(service) // BUSYBOX deluser, err := exec.LookPath("deluser") if err == nil { tryCommand (exec.Command(deluser, fullName, "--remove-home"), "could not delete user") return } // GNU userdel, err := exec.LookPath("userdel") if err == nil { tryCommand (exec.Command(userdel, fullName, "-r"), "could not delete user") groupdel, _ := exec.LookPath("groupdel") tryCommand (exec.Command(groupdel, fullName), "could not delete group") return } printErr("could not delete user: no command deluser or userdel") os.Exit(1) } func execAuth (service, user string) { fullName := serviceUser(service) adduser, err := exec.LookPath("adduser") if err == nil { tryCommand (exec.Command(adduser, user, fullName), "could not add user to group " + fullName) return } // GNU useradd, err := exec.LookPath("usermod") if err == nil { tryCommand (exec.Command(useradd, "-a", "-g", fullName, user), "could not add user to group " + fullName) return } printErr("could not auth user: no command adduser or usermod") os.Exit(1) } func execOwn (service, file string, recurse bool) { fullName := serviceUser(service) userInfo, err := user.Lookup(fullName) uid, _ := strconv.Atoi(userInfo.Uid) gid, _ := strconv.Atoi(userInfo.Gid) if err != nil { printErr("could not get user info: %v", err) os.Exit(1) } if !recurse { ownOne(file, uid, gid) return } err = filepath.Walk(file, func( filePath string, file os.FileInfo, err error, ) error { if err != nil { printErr("could not traverse filesystem: %v", err) return nil } ownOne(filePath, uid, gid) return nil }) if err != nil { printErr("could not traverse filesystem: %v", err) os.Exit(1) } }