diff --git a/main.go b/main.go index 5c4e3c9..0975924 100644 --- a/main.go +++ b/main.go @@ -41,15 +41,26 @@ func main() { log.Fatal(err) } + // Run server and exit + os.Exit(do_main(config)) +} + +func do_main(config Config) int { + // If we are running as root, find the UID of the "nobody" user, before a // chroot() possibly stops seeing /etc/passwd - privInfo := getUserInfo(config) + privInfo, err := getUserInfo(config) + if err != nil { + errorLog.Println("Exiting due to failure to apply security restrictions.") + return 1 + } // Chroot, if asked if config.ChrootDir != "" { err := syscall.Chroot(config.ChrootDir) if err != nil { - log.Fatal("Could not chroot to " + config.ChrootDir + ": " + err.Error()) + log.Println("Could not chroot to " + config.ChrootDir + ": " + err.Error()) + return 1 } } @@ -60,7 +71,8 @@ func main() { } else { errorLogFile, err = os.OpenFile(config.ErrorLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { - log.Fatal(err) + log.Println(err) + return 1 } defer errorLogFile.Close() } @@ -73,7 +85,8 @@ func main() { accessLogFile, err = os.OpenFile(config.AccessLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { errorLog.Println("Error opening access log file: " + err.Error()) - log.Fatal(err) + log.Println(err) + return 1 } defer accessLogFile.Close() } @@ -83,16 +96,16 @@ func main() { info, err := os.Stat(config.KeyPath) if err != nil { errorLog.Println("Error opening TLS key file: " + err.Error()) - log.Fatal(err) + return 1 } if uint64(info.Mode().Perm())&0444 == 0444 { errorLog.Println("Refusing to use world-readable TLS key file " + config.KeyPath) - os.Exit(0) + return 1 } cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath) if err != nil { errorLog.Println("Error loading TLS keypair: " + err.Error()) - log.Fatal(err) + return 1 } tlscfg := &tls.Config{ Certificates: []tls.Certificate{cert}, @@ -108,13 +121,17 @@ func main() { } // Apply security restrictions - enableSecurityRestrictions(config, privInfo, errorLog) + err = enableSecurityRestrictions(config, privInfo, errorLog) + if err != nil { + errorLog.Println("Exiting due to failure to apply security restrictions.") + return 1 + } // Create TLS listener listener, err := tls.Listen("tcp", ":"+strconv.Itoa(config.Port), tlscfg) if err != nil { errorLog.Println("Error creating TLS listener: " + err.Error()) - log.Fatal(err) + return 1 } defer listener.Close() @@ -164,4 +181,6 @@ func main() { wg.Wait() errorLog.Println("Exiting.") + // Exit successfully + return 0 } diff --git a/security.go b/security.go index 5dd86bd..31bc8ae 100644 --- a/security.go +++ b/security.go @@ -10,5 +10,5 @@ import ( // This is intended to be called immediately prior to accepting client // connections and may be used to establish a security "jail" for the molly // brown executable. -func enableSecurityRestrictions(config Config, errorLog *log.Logger) { +func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error { } diff --git a/security_dropprivs.go b/security_dropprivs.go index 3b3a842..050624d 100644 --- a/security_dropprivs.go +++ b/security_dropprivs.go @@ -26,7 +26,7 @@ type userInfo struct { unpriv_gid int } -func getUserInfo(config Config) userInfo { +func getUserInfo(config Config) (userInfo, error) { var ui userInfo ui.uid = os.Getuid() ui.euid = os.Geteuid() @@ -34,7 +34,8 @@ func getUserInfo(config Config) userInfo { ui.egid = os.Getegid() supp_groups, err := os.Getgroups() if err != nil { - log.Fatal("Could not get supplementary groups: ", err.Error()) + log.Println("Could not get supplementary groups: ", err.Error()) + return ui, err } ui.supp_groups = supp_groups ui.unpriv_uid = -1 @@ -55,22 +56,24 @@ func getUserInfo(config Config) userInfo { if ui.need_drop { nobody_user, err := user.Lookup(config.UnprivUsername) if err != nil { - log.Fatal("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error()) + log.Println("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error()) + return ui, err } ui.unpriv_uid, err = strconv.Atoi(nobody_user.Uid) ui.unpriv_gid, err = strconv.Atoi(nobody_user.Gid) if err != nil { - log.Fatal("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error()) + log.Println("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error()) + return ui, err } } - return ui + return ui, nil } -func DropPrivs(ui userInfo, errorLog *log.Logger) { +func DropPrivs(ui userInfo, errorLog *log.Logger) error { // If we're already unprivileged, all good if !ui.need_drop { - return + return nil } // Drop supplementary groups @@ -78,7 +81,7 @@ func DropPrivs(ui userInfo, errorLog *log.Logger) { err := syscall.Setgroups([]int{}) if err != nil { errorLog.Println("Could not unset supplementary groups: " + err.Error()) - log.Fatal(err) + return err } } @@ -87,7 +90,7 @@ func DropPrivs(ui userInfo, errorLog *log.Logger) { err := syscall.Setgid(ui.unpriv_gid) if err != nil { errorLog.Println("Could not setgid to " + strconv.Itoa(ui.unpriv_gid) + ": " + err.Error()) - log.Fatal(err) + return err } } @@ -96,8 +99,9 @@ func DropPrivs(ui userInfo, errorLog *log.Logger) { err := syscall.Setuid(ui.unpriv_uid) if err != nil { errorLog.Println("Could not setuid to " + strconv.Itoa(ui.unpriv_uid) + ": " + err.Error()) - log.Fatal(err) + return err } } + return nil } diff --git a/security_oldgolinux.go b/security_oldgolinux.go index e5f1675..3b0b110 100644 --- a/security_oldgolinux.go +++ b/security_oldgolinux.go @@ -7,7 +7,7 @@ import ( "os" ) -func enableSecurityRestrictions(config Config, errorLog *log.Logger) { +func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error { // Prior to Go 1.6, setuid did not work reliably on Linux // So, absolutely refuse to run as root @@ -16,6 +16,8 @@ func enableSecurityRestrictions(config Config, errorLog *log.Logger) { if uid == 0 || euid == 0 { setuid_err := "Refusing to run with root privileges when setuid() will not work!" errorLog.Println(setuid_err) - log.Fatal(setuid_err) + return error.New(setuid_err) } + + return nil } diff --git a/security_openbsd.go b/security_openbsd.go index 647ce43..3def41f 100644 --- a/security_openbsd.go +++ b/security_openbsd.go @@ -11,17 +11,20 @@ import ( // operations available to the molly brown executable. Please note that (S)CGI // processes that molly brown spawns or communicates with are unrestricted // and should pledge their own restrictions and unveil their own files. -func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) { +func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error { // Setuid to an unprivileged user - DropPrivs(ui, errorLog) + err := DropPrivs(ui, errorLog) + if err != nil { + return err + } // Unveil the configured document base as readable. log.Println("Unveiling \"" + config.DocBase + "\" as readable.") err := unix.Unveil(config.DocBase, "r") if err != nil { errorLog.Println("Could not unveil DocBase: " + err.Error()) - log.Fatal(err) + return err } // Unveil cgi path globs as executable. @@ -32,7 +35,7 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger err = unix.Unveil(cgiGlobbedPath, "rx") if err != nil { errorLog.Println("Could not unveil CGIPaths: " + err.Error()) - log.Fatal(err) + return err } } } @@ -41,6 +44,9 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger for _, scgiSocket := range config.SCGIPaths { log.Println("Unveiling \"" + scgiSocket + "\" as read/write.") err = unix.Unveil(scgiSocket, "rw") + if err != nil { + return err + } } // Finalize the unveil list. @@ -48,7 +54,7 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger err = unix.UnveilBlock() if err != nil { errorLog.Println("Could not block unveil: " + err.Error()) - log.Fatal(err) + return err } // Pledge to only use stdio, inet, and rpath syscalls. @@ -64,6 +70,6 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger err = unix.PledgePromises(promises) if err != nil { errorLog.Println("Could not pledge: " + err.Error()) - log.Fatal(err) + return err } } diff --git a/security_other_unix.go b/security_other_unix.go index 21f4aab..413b444 100644 --- a/security_other_unix.go +++ b/security_other_unix.go @@ -6,8 +6,9 @@ import ( "log" ) -func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) { +func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error { // Setuid to an unprivileged user - DropPrivs(ui, errorLog) + return DropPrivs(ui, errorLog) + }