diff options
author | Damien Miller <djm@mindrot.org> | 2000-04-29 23:57:08 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2000-04-29 23:57:08 +1000 |
commit | eba71bab9bf01c0d688f829a8971f902732558df (patch) | |
tree | a9d5b50568bfc10cc50291fd3604debfaf3e3783 /auth2.c | |
parent | 8117111a3c1360727e3c54aad31aa045e7a7871b (diff) |
- Merge big update to OpenSSH-2.0 from OpenBSD CVS
[README.openssh2]
- interop w/ F-secure windows client
- sync documentation
- ssh_host_dsa_key not ssh_dsa_key
[auth-rsa.c]
- missing fclose
[auth.c authfile.c compat.c dsa.c dsa.h hostfile.c key.c key.h radix.c]
[readconf.c readconf.h ssh-add.c ssh-keygen.c ssh.c ssh.h sshconnect.c]
[sshd.c uuencode.c uuencode.h authfile.h]
- add DSA pubkey auth and other SSH2 fixes. use ssh-keygen -[xX]
for trading keys with the real and the original SSH, directly from the
people who invented the SSH protocol.
[auth.c auth.h authfile.c sshconnect.c auth1.c auth2.c sshconnect.h]
[sshconnect1.c sshconnect2.c]
- split auth/sshconnect in one file per protocol version
[sshconnect2.c]
- remove debug
[uuencode.c]
- add trailing =
[version.h]
- OpenSSH-2.0
[ssh-keygen.1 ssh-keygen.c]
- add -R flag: exit code indicates if RSA is alive
[sshd.c]
- remove unused
silent if -Q is specified
[ssh.h]
- host key becomes /etc/ssh_host_dsa_key
[readconf.c servconf.c ]
- ssh/sshd default to proto 1 and 2
[uuencode.c]
- remove debug
[auth2.c ssh-keygen.c sshconnect2.c sshd.c]
- xfree DSA blobs
[auth2.c serverloop.c session.c]
- cleanup logging for sshd/2, respect PasswordAuth no
[sshconnect2.c]
- less debug, respect .ssh/config
[README.openssh2 channels.c channels.h]
- clientloop.c session.c ssh.c
- support for x11-fwding, client+server
Diffstat (limited to 'auth2.c')
-rw-r--r-- | auth2.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/auth2.c b/auth2.c new file mode 100644 index 000000000..9937ed678 --- /dev/null +++ b/auth2.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. The name of the author may not be used to endorse or promote products | ||
16 | * derived from this software without specific prior written permission. | ||
17 | * | ||
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | #include "includes.h" | ||
30 | RCSID("$OpenBSD: auth2.c,v 1.3 2000/04/27 15:23:02 markus Exp $"); | ||
31 | |||
32 | #include <openssl/dsa.h> | ||
33 | #include <openssl/rsa.h> | ||
34 | #include <openssl/evp.h> | ||
35 | |||
36 | #include "xmalloc.h" | ||
37 | #include "rsa.h" | ||
38 | #include "ssh.h" | ||
39 | #include "pty.h" | ||
40 | #include "packet.h" | ||
41 | #include "buffer.h" | ||
42 | #include "cipher.h" | ||
43 | #include "servconf.h" | ||
44 | #include "compat.h" | ||
45 | #include "channels.h" | ||
46 | #include "bufaux.h" | ||
47 | #include "ssh2.h" | ||
48 | #include "auth.h" | ||
49 | #include "session.h" | ||
50 | #include "dispatch.h" | ||
51 | #include "auth.h" | ||
52 | #include "key.h" | ||
53 | #include "kex.h" | ||
54 | |||
55 | #include "dsa.h" | ||
56 | #include "uidswap.h" | ||
57 | |||
58 | /* import */ | ||
59 | extern ServerOptions options; | ||
60 | extern unsigned char *session_id2; | ||
61 | extern int session_id2_len; | ||
62 | |||
63 | /* protocol */ | ||
64 | |||
65 | void input_service_request(int type, int plen); | ||
66 | void input_userauth_request(int type, int plen); | ||
67 | void protocol_error(int type, int plen); | ||
68 | |||
69 | /* auth */ | ||
70 | int ssh2_auth_none(struct passwd *pw); | ||
71 | int ssh2_auth_password(struct passwd *pw); | ||
72 | int ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen); | ||
73 | |||
74 | /* helper */ | ||
75 | struct passwd* auth_set_user(char *u, char *s); | ||
76 | int user_dsa_key_allowed(struct passwd *pw, Key *key); | ||
77 | |||
78 | typedef struct Authctxt Authctxt; | ||
79 | struct Authctxt { | ||
80 | char *user; | ||
81 | char *service; | ||
82 | struct passwd pw; | ||
83 | int valid; | ||
84 | }; | ||
85 | static Authctxt *authctxt = NULL; | ||
86 | static int userauth_success = 0; | ||
87 | |||
88 | /* | ||
89 | * loop until userauth_success == TRUE | ||
90 | */ | ||
91 | |||
92 | void | ||
93 | do_authentication2() | ||
94 | { | ||
95 | dispatch_init(&protocol_error); | ||
96 | dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); | ||
97 | dispatch_run(DISPATCH_BLOCK, &userauth_success); | ||
98 | do_authenticated2(); | ||
99 | } | ||
100 | |||
101 | void | ||
102 | protocol_error(int type, int plen) | ||
103 | { | ||
104 | log("auth: protocol error: type %d plen %d", type, plen); | ||
105 | packet_start(SSH2_MSG_UNIMPLEMENTED); | ||
106 | packet_put_int(0); | ||
107 | packet_send(); | ||
108 | packet_write_wait(); | ||
109 | } | ||
110 | |||
111 | void | ||
112 | input_service_request(int type, int plen) | ||
113 | { | ||
114 | unsigned int len; | ||
115 | int accept = 0; | ||
116 | char *service = packet_get_string(&len); | ||
117 | packet_done(); | ||
118 | |||
119 | if (strcmp(service, "ssh-userauth") == 0) { | ||
120 | if (!userauth_success) { | ||
121 | accept = 1; | ||
122 | /* now we can handle user-auth requests */ | ||
123 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); | ||
124 | } | ||
125 | } | ||
126 | /* XXX all other service requests are denied */ | ||
127 | |||
128 | if (accept) { | ||
129 | packet_start(SSH2_MSG_SERVICE_ACCEPT); | ||
130 | packet_put_cstring(service); | ||
131 | packet_send(); | ||
132 | packet_write_wait(); | ||
133 | } else { | ||
134 | debug("bad service request %s", service); | ||
135 | packet_disconnect("bad service request %s", service); | ||
136 | } | ||
137 | xfree(service); | ||
138 | } | ||
139 | |||
140 | void | ||
141 | input_userauth_request(int type, int plen) | ||
142 | { | ||
143 | static void (*authlog) (const char *fmt,...) = verbose; | ||
144 | static int attempt = 0; | ||
145 | unsigned int len, rlen; | ||
146 | int authenticated = 0; | ||
147 | char *raw, *user, *service, *method, *authmsg = NULL; | ||
148 | struct passwd *pw; | ||
149 | |||
150 | if (++attempt == AUTH_FAIL_MAX) | ||
151 | packet_disconnect("too many failed userauth_requests"); | ||
152 | |||
153 | raw = packet_get_raw(&rlen); | ||
154 | if (plen != rlen) | ||
155 | fatal("plen != rlen"); | ||
156 | user = packet_get_string(&len); | ||
157 | service = packet_get_string(&len); | ||
158 | method = packet_get_string(&len); | ||
159 | debug("userauth-request for user %s service %s method %s", user, service, method); | ||
160 | |||
161 | /* XXX we only allow the ssh-connection service */ | ||
162 | pw = auth_set_user(user, service); | ||
163 | if (pw && strcmp(service, "ssh-connection")==0) { | ||
164 | if (strcmp(method, "none") == 0) { | ||
165 | authenticated = ssh2_auth_none(pw); | ||
166 | } else if (strcmp(method, "password") == 0) { | ||
167 | authenticated = ssh2_auth_password(pw); | ||
168 | } else if (strcmp(method, "publickey") == 0) { | ||
169 | authenticated = ssh2_auth_pubkey(pw, raw, rlen); | ||
170 | } | ||
171 | } | ||
172 | if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) { | ||
173 | authenticated = 0; | ||
174 | log("ROOT LOGIN REFUSED FROM %.200s", | ||
175 | get_canonical_hostname()); | ||
176 | } | ||
177 | |||
178 | #ifdef USE_PAM | ||
179 | if (authenticated && !do_pam_account(pw->pw_name, NULL)) | ||
180 | authenticated = 0; | ||
181 | #endif /* USE_PAM */ | ||
182 | |||
183 | /* XXX todo: check if multiple auth methods are needed */ | ||
184 | if (authenticated == 1) { | ||
185 | authmsg = "Accepted"; | ||
186 | /* turn off userauth */ | ||
187 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); | ||
188 | packet_start(SSH2_MSG_USERAUTH_SUCCESS); | ||
189 | packet_send(); | ||
190 | packet_write_wait(); | ||
191 | /* now we can break out */ | ||
192 | userauth_success = 1; | ||
193 | } else if (authenticated == 0) { | ||
194 | authmsg = "Failed"; | ||
195 | packet_start(SSH2_MSG_USERAUTH_FAILURE); | ||
196 | packet_put_cstring("publickey,password"); /* XXX dynamic */ | ||
197 | packet_put_char(0); /* XXX partial success, unused */ | ||
198 | packet_send(); | ||
199 | packet_write_wait(); | ||
200 | } else { | ||
201 | authmsg = "Postponed"; | ||
202 | } | ||
203 | /* Raise logging level */ | ||
204 | if (authenticated == 1|| | ||
205 | attempt == AUTH_FAIL_LOG || | ||
206 | strcmp(method, "password") == 0) | ||
207 | authlog = log; | ||
208 | |||
209 | authlog("%s %s for %.200s from %.200s port %d ssh2", | ||
210 | authmsg, | ||
211 | method, | ||
212 | pw && pw->pw_uid == 0 ? "ROOT" : user, | ||
213 | get_remote_ipaddr(), | ||
214 | get_remote_port()); | ||
215 | |||
216 | xfree(service); | ||
217 | xfree(user); | ||
218 | xfree(method); | ||
219 | } | ||
220 | |||
221 | int | ||
222 | ssh2_auth_none(struct passwd *pw) | ||
223 | { | ||
224 | packet_done(); | ||
225 | #ifdef USE_PAM | ||
226 | return auth_pam_password(pw, ""); | ||
227 | #else /* USE_PAM */ | ||
228 | return auth_password(pw, ""); | ||
229 | #endif /* USE_PAM */ | ||
230 | } | ||
231 | int | ||
232 | ssh2_auth_password(struct passwd *pw) | ||
233 | { | ||
234 | char *password; | ||
235 | int authenticated = 0; | ||
236 | int change; | ||
237 | unsigned int len; | ||
238 | change = packet_get_char(); | ||
239 | if (change) | ||
240 | log("password change not supported"); | ||
241 | password = packet_get_string(&len); | ||
242 | packet_done(); | ||
243 | if (options.password_authentication && | ||
244 | #ifdef USE_PAM | ||
245 | auth_pam_password(pw, password) == 1) | ||
246 | #else /* USE_PAM */ | ||
247 | auth_password(pw, password) == 1) | ||
248 | #endif /* USE_PAM */ | ||
249 | authenticated = 1; | ||
250 | memset(password, 0, len); | ||
251 | xfree(password); | ||
252 | return authenticated; | ||
253 | } | ||
254 | int | ||
255 | ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen) | ||
256 | { | ||
257 | Buffer b; | ||
258 | Key *key; | ||
259 | char *pkalg, *pkblob, *sig; | ||
260 | unsigned int alen, blen, slen; | ||
261 | int have_sig; | ||
262 | int authenticated = 0; | ||
263 | |||
264 | if (options.rsa_authentication == 0) { | ||
265 | debug("pubkey auth disabled"); | ||
266 | return 0; | ||
267 | } | ||
268 | have_sig = packet_get_char(); | ||
269 | pkalg = packet_get_string(&alen); | ||
270 | if (strcmp(pkalg, KEX_DSS) != 0) { | ||
271 | xfree(pkalg); | ||
272 | log("bad pkalg %s", pkalg); /*XXX*/ | ||
273 | return 0; | ||
274 | } | ||
275 | pkblob = packet_get_string(&blen); | ||
276 | key = dsa_key_from_blob(pkblob, blen); | ||
277 | if (key != NULL) { | ||
278 | if (have_sig) { | ||
279 | sig = packet_get_string(&slen); | ||
280 | packet_done(); | ||
281 | buffer_init(&b); | ||
282 | buffer_append(&b, session_id2, session_id2_len); | ||
283 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | ||
284 | if (slen + 4 > rlen) | ||
285 | fatal("bad rlen/slen"); | ||
286 | buffer_append(&b, raw, rlen - slen - 4); | ||
287 | #ifdef DEBUG_DSS | ||
288 | buffer_dump(&b); | ||
289 | #endif | ||
290 | /* test for correct signature */ | ||
291 | if (user_dsa_key_allowed(pw, key) && | ||
292 | dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) | ||
293 | authenticated = 1; | ||
294 | buffer_clear(&b); | ||
295 | xfree(sig); | ||
296 | } else { | ||
297 | packet_done(); | ||
298 | debug("test key..."); | ||
299 | /* test whether pkalg/pkblob are acceptable */ | ||
300 | /* XXX fake reply and always send PK_OK ? */ | ||
301 | if (user_dsa_key_allowed(pw, key)) { | ||
302 | packet_start(SSH2_MSG_USERAUTH_PK_OK); | ||
303 | packet_put_string(pkalg, alen); | ||
304 | packet_put_string(pkblob, blen); | ||
305 | packet_send(); | ||
306 | packet_write_wait(); | ||
307 | authenticated = -1; | ||
308 | } | ||
309 | } | ||
310 | key_free(key); | ||
311 | } | ||
312 | xfree(pkalg); | ||
313 | xfree(pkblob); | ||
314 | return authenticated; | ||
315 | } | ||
316 | |||
317 | /* set and get current user */ | ||
318 | |||
319 | struct passwd* | ||
320 | auth_get_user(void) | ||
321 | { | ||
322 | return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; | ||
323 | } | ||
324 | |||
325 | struct passwd* | ||
326 | auth_set_user(char *u, char *s) | ||
327 | { | ||
328 | struct passwd *pw, *copy; | ||
329 | |||
330 | if (authctxt == NULL) { | ||
331 | authctxt = xmalloc(sizeof(*authctxt)); | ||
332 | authctxt->valid = 0; | ||
333 | authctxt->user = xstrdup(u); | ||
334 | authctxt->service = xstrdup(s); | ||
335 | setproctitle("%s", u); | ||
336 | pw = getpwnam(u); | ||
337 | if (!pw || !allowed_user(pw)) { | ||
338 | log("auth_set_user: illegal user %s", u); | ||
339 | return NULL; | ||
340 | } | ||
341 | #ifdef USE_PAM | ||
342 | start_pam(pw); | ||
343 | #endif | ||
344 | copy = &authctxt->pw; | ||
345 | memset(copy, 0, sizeof(*copy)); | ||
346 | copy->pw_name = xstrdup(pw->pw_name); | ||
347 | copy->pw_passwd = xstrdup(pw->pw_passwd); | ||
348 | copy->pw_uid = pw->pw_uid; | ||
349 | copy->pw_gid = pw->pw_gid; | ||
350 | copy->pw_dir = xstrdup(pw->pw_dir); | ||
351 | copy->pw_shell = xstrdup(pw->pw_shell); | ||
352 | authctxt->valid = 1; | ||
353 | } else { | ||
354 | if (strcmp(u, authctxt->user) != 0 || | ||
355 | strcmp(s, authctxt->service) != 0) { | ||
356 | log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", | ||
357 | u, s, authctxt->user, authctxt->service); | ||
358 | return NULL; | ||
359 | } | ||
360 | } | ||
361 | return auth_get_user(); | ||
362 | } | ||
363 | |||
364 | /* return 1 if user allows given key */ | ||
365 | int | ||
366 | user_dsa_key_allowed(struct passwd *pw, Key *key) | ||
367 | { | ||
368 | char line[8192], file[1024]; | ||
369 | int found_key = 0; | ||
370 | unsigned int bits = -1; | ||
371 | FILE *f; | ||
372 | unsigned long linenum = 0; | ||
373 | struct stat st; | ||
374 | Key *found; | ||
375 | |||
376 | /* Temporarily use the user's uid. */ | ||
377 | temporarily_use_uid(pw->pw_uid); | ||
378 | |||
379 | /* The authorized keys. */ | ||
380 | snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, | ||
381 | SSH_USER_PERMITTED_KEYS2); | ||
382 | |||
383 | /* Fail quietly if file does not exist */ | ||
384 | if (stat(file, &st) < 0) { | ||
385 | /* Restore the privileged uid. */ | ||
386 | restore_uid(); | ||
387 | return 0; | ||
388 | } | ||
389 | /* Open the file containing the authorized keys. */ | ||
390 | f = fopen(file, "r"); | ||
391 | if (!f) { | ||
392 | /* Restore the privileged uid. */ | ||
393 | restore_uid(); | ||
394 | return 0; | ||
395 | } | ||
396 | if (options.strict_modes) { | ||
397 | int fail = 0; | ||
398 | char buf[1024]; | ||
399 | /* Check open file in order to avoid open/stat races */ | ||
400 | if (fstat(fileno(f), &st) < 0 || | ||
401 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
402 | (st.st_mode & 022) != 0) { | ||
403 | snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: " | ||
404 | "bad ownership or modes for '%s'.", pw->pw_name, file); | ||
405 | fail = 1; | ||
406 | } else { | ||
407 | /* Check path to SSH_USER_PERMITTED_KEYS */ | ||
408 | int i; | ||
409 | static const char *check[] = { | ||
410 | "", SSH_USER_DIR, NULL | ||
411 | }; | ||
412 | for (i = 0; check[i]; i++) { | ||
413 | snprintf(line, sizeof line, "%.500s/%.100s", | ||
414 | pw->pw_dir, check[i]); | ||
415 | if (stat(line, &st) < 0 || | ||
416 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
417 | (st.st_mode & 022) != 0) { | ||
418 | snprintf(buf, sizeof buf, | ||
419 | "DSA authentication refused for %.100s: " | ||
420 | "bad ownership or modes for '%s'.", | ||
421 | pw->pw_name, line); | ||
422 | fail = 1; | ||
423 | break; | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | if (fail) { | ||
428 | log(buf); | ||
429 | fclose(f); | ||
430 | restore_uid(); | ||
431 | return 0; | ||
432 | } | ||
433 | } | ||
434 | found_key = 0; | ||
435 | found = key_new(KEY_DSA); | ||
436 | |||
437 | while (fgets(line, sizeof(line), f)) { | ||
438 | char *cp; | ||
439 | linenum++; | ||
440 | /* Skip leading whitespace, empty and comment lines. */ | ||
441 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
442 | ; | ||
443 | if (!*cp || *cp == '\n' || *cp == '#') | ||
444 | continue; | ||
445 | bits = key_read(found, &cp); | ||
446 | if (bits == 0) | ||
447 | continue; | ||
448 | if (key_equal(found, key)) { | ||
449 | found_key = 1; | ||
450 | debug("matching key found: file %s, line %ld", | ||
451 | file, linenum); | ||
452 | break; | ||
453 | } | ||
454 | } | ||
455 | restore_uid(); | ||
456 | fclose(f); | ||
457 | key_free(found); | ||
458 | return found_key; | ||
459 | } | ||