diff options
Diffstat (limited to 'client/cmd.go')
-rw-r--r-- | client/cmd.go | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/client/cmd.go b/client/cmd.go new file mode 100644 index 0000000..b69880c --- /dev/null +++ b/client/cmd.go @@ -0,0 +1,131 @@ +package client + +import ( + "context" + "fmt" + "io" + "os/exec" + "strings" +) + +func (c client) princExists(ctx context.Context, name string) (exists bool, err error) { + out, err := c.execKrbAdmin(ctx, fmt.Sprintf("getprinc \"%s\"", name), func(writer io.WriteCloser) error { return nil }) + if err != nil { + err = fmt.Errorf("%s (output: %s)", err, out) + return + } + + if strings.Contains(out, "Principal does not exist") { + exists = false + } else if strings.Contains(out, "Expiration date: ") { + exists = true + } else { + err = fmt.Errorf("unrecognised output format: %s", out) + } + + return +} + +func (c client) doCreatePrinc(ctx context.Context, name string, password string) (err error) { + out, err := c.execKrbAdmin(ctx, fmt.Sprintf("addprinc \"%s\"", name), func(writer io.WriteCloser) error { + toWrite := append([]byte(password), '\n') + for i := 0; i < 2; i++ { + n, err := writer.Write(toWrite) + if err != nil || n != len(toWrite) { + return fmt.Errorf("error writing to stdin: %s", err) + } + } + return nil + }) + + if err != nil { + return + } + + if !strings.Contains(out, "created") { + err = fmt.Errorf("unrecognised output format: %s", out) + } + + return +} + +func (c client) doChangePassword(ctx context.Context, name string, password string) (err error) { + out, err := c.execKrbAdmin(ctx, fmt.Sprintf("cpw \"%s\"", name), func(writer io.WriteCloser) error { + toWrite := append([]byte(password), '\n') + for i := 0; i < 2; i++ { + n, err := writer.Write(toWrite) + if err != nil || n != len(toWrite) { + return fmt.Errorf("error writing to stdin: %s", err) + } + } + return nil + }) + + if err != nil { + return + } + + if !strings.Contains(out, "changed") { + err = fmt.Errorf("unrecognised output format: %s", out) + } + + return +} + +// execKrbAdmin starts krbadmin with the appropriate commands, and tries to authenticate as the admin principal +func (c client) execKrbAdmin(ctx context.Context, query string, writeFunc func(writer io.WriteCloser) error) (string, error) { + kadm, err := exec.LookPath("kadmin") + if err != nil { + return "", fmt.Errorf("error finding kadmin executable: %s", err) + } + cmd := exec.CommandContext( + ctx, + kadm, + "-p", + c.config.Username, + "-r", + c.config.Realm, + "-s", + c.config.KAdminServer, + "-q", + query, + ) + + stdin, err := cmd.StdinPipe() + if err != nil { + return "", fmt.Errorf("error getting stdin pipe: %s", err) + } + + errChan := make(chan error) + + go func() { + defer stdin.Close() + toWrite := append([]byte(c.config.Password), '\n') + n, err := stdin.Write(toWrite) + + if err != nil || n != len(toWrite) { + errChan <- fmt.Errorf("error writing to stdin: %s", err) + return + } + + err = writeFunc(stdin) + if err != nil || n != len(toWrite) { + errChan <- fmt.Errorf("error writing to stdin: %s", err) + return + } + }() + + rawOut, err := cmd.CombinedOutput() + if err != nil { + return "", err + } + out := string(rawOut) + + select { + case err = <-errChan: + return "", err + default: + } + + return out, nil +} |