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