]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Refuse to run checkpassword script insecurely by default
authorTimo Sirainen <tss@iki.fi>
Sat, 26 Oct 2013 16:11:34 +0000 (19:11 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 26 Oct 2013 16:11:34 +0000 (19:11 +0300)
src/auth/checkpassword-reply.c
src/auth/db-checkpassword.c

index f3d2e20b2eb57e5bac6b73225656445021a01e2c..31bf3473b1f380b1ca5f6528c4865cf90d6a0052 100644 (file)
 int main(void)
 {
        string_t *str;
-       const char *user, *home, *authorized;
+       const char *user, *home, *authorized, *orig_uid;
        const char *extra_env, *key, *value, *const *tmp;
        bool uid_found = FALSE, gid_found = FALSE;
 
        lib_init();
        str = t_str_new(1024);
 
+       orig_uid = getenv("ORIG_UID");
+       /* ORIG_UID should have the auth process's UID that forked us.
+          if the checkpassword changed the UID, this could be a security hole
+          because the UID's other processes can ptrace this process and write
+          any kind of a reply to fd 4. so we can run only if:
+
+          a) INSECURE_SETUID environment is set.
+          b) process isn't ptraceable (this binary is setuid/setgid)
+          c) checkpassword didn't actually change the UID (but used
+             userdb_uid instead)
+          */
+       if (getenv("INSECURE_SETUID") == NULL &&
+           (orig_uid == NULL || strtoul(orig_uid, NULL, 10) != getuid()) &&
+           getuid() == geteuid() && getgid() == getegid()) {
+               if (orig_uid == NULL) {
+                       i_error("checkpassword: ORIG_UID environment was dropped by checkpassword. "
+                               "Can't verify if we're safe to run. See "
+                               "http://wiki2.dovecot.org/AuthDatabase/CheckPassword#Security");
+               } else {
+                       i_error("checkpassword: The checkpassword couldn't be run securely. See "
+                               "http://wiki2.dovecot.org/AuthDatabase/CheckPassword#Security");
+               }
+               return 111;
+       }
+
        user = getenv("USER");
        if (user != NULL) {
                if (strchr(user, '\t') != NULL) {
index 3c8b8a1d2f47155c9e7c5dd8dec9b9668b9e7433..a5a1e7f7d152c240bc731fb351373b3deb99cb47 100644 (file)
@@ -251,6 +251,7 @@ static void checkpassword_setup_env(struct auth_request *request)
           pipe, also pass some other possibly interesting information
           via environment. Use UCSPI names for local/remote IPs. */
        env_put("PROTO=TCP"); /* UCSPI */
+       env_put(t_strdup_printf("ORIG_UID=%s", dec2str(getuid())));
        env_put(t_strconcat("SERVICE=", request->service, NULL));
        if (request->local_ip.family != 0) {
                env_put(t_strconcat("TCPLOCALIP=",