Check system passwords for * --- busybox-1.19.0/networking/httpd.c +++ busybox-1.19.0/networking/httpd.c @@ -54,6 +54,8 @@ * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/ * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/ * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/ + * /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/ + * /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/ * .au:audio/basic # additional mime type for audio.au files * *.php:/path/php # run xxx.php through an interpreter * @@ -123,6 +125,14 @@ //usage: "\n -d STRING URL decode STRING" #include "libbb.h" +#if ENABLE_PAM +/* PAM may include . We may need to undefine bbox's stub define: */ +# undef setlocale +/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx. + * Apparently they like to confuse people. */ +# include +# include +#endif #if ENABLE_FEATURE_HTTPD_USE_SENDFILE # include #endif @@ -1730,6 +1740,57 @@ } #if ENABLE_FEATURE_HTTPD_BASIC_AUTH + +#if ENABLE_FEATURE_HTTPD_AUTH_MD5 && ENABLE_PAM +struct pam_userinfo { + const char *name; + const char *pw; +}; + +static int pam_talker(int num_msg, + const struct pam_message ** msg, + struct pam_response ** resp, + void *appdata_ptr) +{ + int i; + struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr; + struct pam_response *response; + + if (!resp || !msg || !userinfo) + return PAM_CONV_ERR; + + /* allocate memory to store response */ + response = malloc(num_msg * sizeof(struct pam_response)); + if (!response) + return PAM_CONV_ERR; + + /* copy values */ + for (i = 0; i < num_msg; i++) { + const char *s; + + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_ON: + s = userinfo->name; + break; + case PAM_PROMPT_ECHO_OFF: + s = userinfo->pw; + break; + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + s = ""; + break; + default: + free(response); + return PAM_CONV_ERR; + } + response[i].resp = strdup(s); + response[i].resp_retcode = PAM_SUCCESS; + } + *resp = response; + return PAM_SUCCESS; +} +#endif + /* * Config file entries are of the form "/::". * If config file has no prefix match for path, access is allowed. @@ -1747,6 +1808,7 @@ for (cur = g_auth; cur; cur = cur->next) { const char *dir_prefix; size_t len; + int r; dir_prefix = cur->before_colon; @@ -1771,36 +1833,78 @@ prev = dir_prefix; if (ENABLE_FEATURE_HTTPD_AUTH_MD5) { - char *md5_passwd; + const char *unencrypted = strchr(user_and_passwd, ':') + 1; + const char *passwd = strchr(cur->after_colon, ':'); + int user_len_p1 = unencrypted - user_and_passwd; + char username[256]; + + strncpy(username, user_and_passwd, user_len_p1); + username[user_len_p1 - 1] = 0; + if (passwd && passwd[1] == '*') { +#if ENABLE_PAM + struct pam_userinfo userinfo; + struct pam_conv conv_info = {&pam_talker, (void *) &userinfo}; + pam_handle_t *pamh; + + userinfo.name = username; + userinfo.pw = unencrypted; - md5_passwd = strchr(cur->after_colon, ':'); - if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1' - && md5_passwd[3] == '$' && md5_passwd[4] + if (cur->after_colon[0] != '*' && + strncmp(username,cur->after_colon,user_len_p1 - 1) != 0) + continue; + r = pam_start("httpd", username, &conv_info, &pamh) != PAM_SUCCESS + || pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS + || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS; + pam_end(pamh, PAM_SUCCESS); + goto end_check_passwd; +#else +#if ENABLE_FEATURE_SHADOWPASSWDS + /* Using _r function to avoid pulling in static buffers */ + struct spwd spw; + char buffer[256]; +#endif + struct passwd *pw = getpwnam(username); + if (!pw || !pw->pw_passwd) + continue; + passwd = pw->pw_passwd; +#if ENABLE_FEATURE_SHADOWPASSWDS + if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) { + /* getspnam_r may return 0 yet set result to NULL. + * At least glibc 2.4 does this. Be extra paranoid here. */ + struct spwd *result = NULL; + r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result); + if (!r && result) + passwd = result->sp_pwdp; + } +#endif + passwd--; +#endif // ENABLE_PAM + } + if (passwd && passwd[1] == '$' && passwd[2] == '1' + && passwd[3] == '$' && passwd[4] ) { char *encrypted; - int r, user_len_p1; - - md5_passwd++; - user_len_p1 = md5_passwd - cur->after_colon; + + passwd++; /* comparing "user:" */ - if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) { + if (cur->after_colon[0] != '*' && + strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) { continue; } encrypted = pw_encrypt( - user_and_passwd + user_len_p1 /* cleartext pwd from user */, - md5_passwd /*salt */, 1 /* cleanup */); - r = strcmp(encrypted, md5_passwd); + unencrypted /* cleartext pwd from user */, + passwd /*salt */, 1 /* cleanup */); + r = strcmp(encrypted, passwd); free(encrypted); - if (r == 0) - goto set_remoteuser_var; /* Ok */ - continue; + goto end_check_passwd; } } /* Comparing plaintext "user:pass" in one go */ - if (strcmp(cur->after_colon, user_and_passwd) == 0) { - set_remoteuser_var: + r = strcmp(cur->after_colon, user_and_passwd); +end_check_passwd: + if (r == 0) { remoteuser = xstrndup(user_and_passwd, strchrnul(user_and_passwd, ':') - user_and_passwd); return 1; /* Ok */