wok-4.x view busybox/stuff/busybox-1.18-httpd.u @ rev 11356

busybox/httpd: add support for system passwords (again)
author Pascal Bellard <pascal.bellard@slitaz.org>
date Thu Dec 01 21:53:35 2011 +0100 (2011-12-01)
parents c2af9b1a616f
children 4b603f180e5d
line source
1 Add support for system passwords
2 --- busybox-1.18.2/networking/httpd.c
3 +++ busybox-1.18.2/networking/httpd.c
4 @@ -50,6 +50,8 @@
5 * /cgi-bin:foo:bar # Require user foo, pwd bar on urls starting with /cgi-bin/
6 * /adm:admin:setup # Require user admin, pwd setup on urls starting with /adm/
7 * /adm:toor:PaSsWd # or user toor, pwd PaSsWd on urls starting with /adm/
8 + * /adm:root:* # or user root, pwd from /etc/passwd on urls starting with /adm/
9 + * /wiki:*:* # or any user from /etc/passwd with according pwd on urls starting with /wiki/
10 * .au:audio/basic # additional mime type for audio.au files
11 * *.php:/path/php # run xxx.php through an interpreter
12 *
13 @@ -94,6 +96,14 @@
14 /* TODO: use TCP_CORK, parse_config() */
16 #include "libbb.h"
17 +#if ENABLE_PAM
18 +/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
19 +# undef setlocale
20 +/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
21 + * Apparently they like to confuse people. */
22 +# include <security/pam_appl.h>
23 +# include <security/pam_misc.h>
24 +#endif
25 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
26 # include <sys/sendfile.h>
27 #endif
28 @@ -1700,6 +1710,56 @@
29 }
31 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
32 +
33 +# if ENABLE_FEATURE_HTTPD_AUTH_MD5 && ENABLE_PAM
34 +struct pam_userinfo {
35 + const char *name;
36 + const char *pw;
37 +};
38 +
39 +static int pam_talker(int num_msg,
40 + const struct pam_message **msg,
41 + struct pam_response **resp,
42 + void *appdata_ptr)
43 +{
44 + int i;
45 + struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
46 + struct pam_response *response;
47 +
48 + if (!resp || !msg || !userinfo)
49 + return PAM_CONV_ERR;
50 +
51 + /* allocate memory to store response */
52 + response = xzalloc(num_msg * sizeof(*response));
53 +
54 + /* copy values */
55 + for (i = 0; i < num_msg; i++) {
56 + const char *s;
57 +
58 + switch (msg[i]->msg_style) {
59 + case PAM_PROMPT_ECHO_ON:
60 + s = userinfo->name;
61 + break;
62 + case PAM_PROMPT_ECHO_OFF:
63 + s = userinfo->pw;
64 + break;
65 + case PAM_ERROR_MSG:
66 + case PAM_TEXT_INFO:
67 + s = "";
68 + break;
69 + default:
70 + free(response);
71 + return PAM_CONV_ERR;
72 + }
73 + response[i].resp = xstrdup(s);
74 + if (PAM_SUCCESS != 0)
75 + response[i].resp_retcode = PAM_SUCCESS;
76 + }
77 + *resp = response;
78 + return PAM_SUCCESS;
79 +}
80 +# endif
81 +
82 /*
83 * Config file entries are of the form "/<path>:<user>:<passwd>".
84 * If config file has no prefix match for path, access is allowed.
85 @@ -1709,7 +1769,7 @@
86 *
87 * Returns 1 if user_and_passwd is OK.
88 */
89 -static int check_user_passwd(const char *path, const char *user_and_passwd)
90 +static int check_user_passwd(const char *path, char *user_and_passwd)
91 {
92 Htaccess *cur;
93 const char *prev = NULL;
94 @@ -1717,6 +1777,7 @@
95 for (cur = g_auth; cur; cur = cur->next) {
96 const char *dir_prefix;
97 size_t len;
98 + int r;
100 dir_prefix = cur->before_colon;
102 @@ -1741,36 +1802,96 @@
103 prev = dir_prefix;
105 if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
106 - char *md5_passwd;
107 + char *colon_after_user;
108 + const char *passwd;
109 +# if ENABLE_FEATURE_SHADOWPASSWDS && !ENABLE_PAM
110 + char sp_buf[256];
111 +# endif
113 - md5_passwd = strchr(cur->after_colon, ':');
114 - if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1'
115 - && md5_passwd[3] == '$' && md5_passwd[4]
116 - ) {
117 - char *encrypted;
118 - int r, user_len_p1;
119 + colon_after_user = strchr(user_and_passwd, ':');
120 + if (!colon_after_user)
121 + goto bad_input;
122 + passwd = strchr(cur->after_colon, ':');
123 + if (!passwd)
124 + goto bad_input;
125 + passwd++;
126 + if (passwd[0] == '*') {
127 +# if ENABLE_PAM
128 + struct pam_userinfo userinfo;
129 + struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
130 + pam_handle_t *pamh;
132 - md5_passwd++;
133 - user_len_p1 = md5_passwd - cur->after_colon;
134 - /* comparing "user:" */
135 - if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) {
136 + /* compare "user:" */
137 + if (cur->after_colon[0] != '*'
138 + && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0
139 + ) {
140 continue;
141 }
142 + /* this cfg entry is '*' or matches username from peer */
143 + *colon_after_user = '\0';
144 + userinfo.name = user_and_passwd;
145 + userinfo.pw = colon_after_user + 1;
146 + r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS;
147 + if (r == 0) {
148 + r = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
149 + || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
150 + ;
151 + pam_end(pamh, PAM_SUCCESS);
152 + }
153 + *colon_after_user = ':';
154 + goto end_check_passwd;
155 +# else
156 +# if ENABLE_FEATURE_SHADOWPASSWDS
157 + /* Using _r function to avoid pulling in static buffers */
158 + struct spwd spw;
159 +# endif
160 + struct passwd *pw;
162 - encrypted = pw_encrypt(
163 - user_and_passwd + user_len_p1 /* cleartext pwd from user */,
164 - md5_passwd /*salt */, 1 /* cleanup */);
165 - r = strcmp(encrypted, md5_passwd);
166 - free(encrypted);
167 - if (r == 0)
168 - goto set_remoteuser_var; /* Ok */
169 + *colon_after_user = '\0';
170 + pw = getpwnam(user_and_passwd);
171 + *colon_after_user = ':';
172 + if (!pw || !pw->pw_passwd)
173 + continue;
174 + passwd = pw->pw_passwd;
175 +# if ENABLE_FEATURE_SHADOWPASSWDS
176 + if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
177 + /* getspnam_r may return 0 yet set result to NULL.
178 + * At least glibc 2.4 does this. Be extra paranoid here. */
179 + struct spwd *result = NULL;
180 + r = getspnam_r(pw->pw_name, &spw, sp_buf, sizeof(sp_buf), &result);
181 + if (r == 0 && result)
182 + passwd = result->sp_pwdp;
183 + }
184 +# endif
185 +# endif /* ENABLE_PAM */
186 + }
187 +
188 + /* compare "user:" */
189 + if (cur->after_colon[0] != '*'
190 + && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0
191 + ) {
192 continue;
193 }
194 + /* this cfg entry is '*' or matches username from peer */
195 +
196 + /* encrypt pwd from peer and check match with local one */
197 + {
198 + char *encrypted = pw_encrypt(
199 + /* pwd: */ colon_after_user + 1,
200 + /* salt: */ passwd,
201 + /* cleanup: */ 0
202 + );
203 + r = strcmp(encrypted, passwd);
204 + free(encrypted);
205 + goto end_check_passwd;
206 + }
207 + bad_input: ;
208 }
210 /* Comparing plaintext "user:pass" in one go */
211 - if (strcmp(cur->after_colon, user_and_passwd) == 0) {
212 - set_remoteuser_var:
213 + r = strcmp(cur->after_colon, user_and_passwd);
214 + end_check_passwd:
215 + if (r == 0) {
216 remoteuser = xstrndup(user_and_passwd,
217 strchrnul(user_and_passwd, ':') - user_and_passwd);
218 return 1; /* Ok */
219 @@ -2112,7 +2233,7 @@
220 /* Case: no "Authorization:" was seen, but page does require passwd.
221 * Check that with dummy user:pass */
222 if (authorized < 0)
223 - authorized = check_user_passwd(urlcopy, ":");
224 + authorized = check_user_passwd(urlcopy, (char *) "");
225 if (!authorized)
226 send_headers_and_exit(HTTP_UNAUTHORIZED);
227 #endif