diff options
| author | Ophestra Umiker <cat@ophivana.moe> | 2024-11-17 02:03:18 +0900 |
|---|---|---|
| committer | Ophestra Umiker <cat@ophivana.moe> | 2024-11-17 02:16:02 +0900 |
| commit | 6a6d30af1f15b0aa1b79174ecb08b59bd77dabb0 (patch) | |
| tree | a545af721e9bced58e6bb9ce7b8f08fa72c455bc /cmd | |
| parent | df33123bd7f1e0cb4e98580b7e63818c82aa7206 (diff) | |
cmd/fuserdb: systemd userdb drop-in entries generator
This provides user records via nss-systemd. Static drop-in entries are generated to reduce complexity and attack surface.
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/fuserdb/main.go | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/cmd/fuserdb/main.go b/cmd/fuserdb/main.go new file mode 100644 index 00000000..9283d82c --- /dev/null +++ b/cmd/fuserdb/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "flag" + "fmt" + "os" + "path" + "strconv" + + "git.ophivana.moe/security/fortify/internal/fmsg" +) + +func main() { + fmsg.SetPrefix("fuserdb") + + const varEmpty = "/var/empty" + + out := flag.String("o", "userdb", "output directory") + homeDir := flag.String("d", varEmpty, "parent of home directories") + shell := flag.String("s", "/sbin/nologin", "absolute path to subordinate user shell") + flag.Parse() + + type user struct { + name string + fid int + } + + users := make([]user, len(flag.Args())) + for i, s := range flag.Args() { + f := bytes.SplitN([]byte(s), []byte{':'}, 2) + if len(f) != 2 { + fmsg.Fatalf("invalid entry at index %d", i) + } + users[i].name = string(f[0]) + if fid, err := strconv.Atoi(string(f[1])); err != nil { + fmsg.Fatal(err.Error()) + } else { + users[i].fid = fid + } + } + + if err := os.MkdirAll(*out, 0755); err != nil && !errors.Is(err, os.ErrExist) { + fmsg.Fatalf("cannot create output: %v", err) + } + + type payload struct { + UserName string `json:"userName"` + Uid int `json:"uid"` + Gid int `json:"gid"` + RealName string `json:"realName"` + HomeDirectory string `json:"homeDirectory"` + Shell string `json:"shell"` + } + + for _, u := range users { + fidString := strconv.Itoa(u.fid) + for aid := 0; aid < 9999; aid++ { + userName := fmt.Sprintf("u%d_a%d", u.fid, aid) + uid := 1000000 + u.fid*10000 + aid + us := strconv.Itoa(uid) + realName := fmt.Sprintf("Fortify subordinate user %d (%s)", aid, u.name) + var homeDirectory string + if *homeDir != varEmpty { + homeDirectory = path.Join(*homeDir, fidString, strconv.Itoa(aid)) + } else { + homeDirectory = varEmpty + } + + fileName := userName + ".user" + if f, err := os.OpenFile(path.Join(*out, fileName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil { + fmsg.Fatalf("cannot create %s: %v", userName, err) + } else if err = json.NewEncoder(f).Encode(&payload{ + UserName: userName, + Uid: uid, + Gid: uid, + RealName: realName, + HomeDirectory: homeDirectory, + Shell: *shell, + }); err != nil { + fmsg.Fatalf("cannot serialise %s: %v", userName, err) + } else if err = f.Close(); err != nil { + fmsg.Printf("cannot close %s: %v", userName, err) + } + if err := os.Symlink(fileName, path.Join(*out, us+".user")); err != nil { + fmsg.Fatalf("cannot link %s: %v", userName, err) + } + } + } + + fmsg.Printf("created %d entries", len(users)*10000) + fmsg.Exit(0) +} |
