Annotation of src/usr.bin/ssh/auth2.c, Revision 1.2
1.1 markus 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"
1.2 ! markus 30: RCSID("$OpenBSD: auth2.c,v 1.1 2000/04/26 21:28:32 markus Exp $");
1.1 markus 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 int try = 0;
144: unsigned int len, rlen;
145: int authenticated = 0;
146: char *raw, *user, *service, *method;
147: struct passwd *pw;
148:
149: if (++try == AUTH_FAIL_MAX)
150: packet_disconnect("too many failed userauth_requests");
151:
152: raw = packet_get_raw(&rlen);
153: if (plen != rlen)
154: fatal("plen != rlen");
155: user = packet_get_string(&len);
156: service = packet_get_string(&len);
157: method = packet_get_string(&len);
158: debug("userauth-request for user %s service %s method %s", user, service, method);
159:
160: /* XXX we only allow the ssh-connection service */
161: pw = auth_set_user(user, service);
162: if (pw && strcmp(service, "ssh-connection")==0) {
163: if (strcmp(method, "none") == 0) {
164: authenticated = ssh2_auth_none(pw);
165: } else if (strcmp(method, "password") == 0) {
166: authenticated = ssh2_auth_password(pw);
167: } else if (strcmp(method, "publickey") == 0) {
168: authenticated = ssh2_auth_pubkey(pw, raw, rlen);
169: }
170: }
171: /* XXX check if other auth methods are needed */
172: if (authenticated == 1) {
173: log("userauth success for %s method %s", user, method);
174: /* turn off userauth */
175: dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
176: packet_start(SSH2_MSG_USERAUTH_SUCCESS);
177: packet_send();
178: packet_write_wait();
179: /* now we can break out */
180: userauth_success = 1;
181: } else if (authenticated == 0) {
182: log("userauth failure for %s method %s", user, method);
183: packet_start(SSH2_MSG_USERAUTH_FAILURE);
184: packet_put_cstring("publickey,password"); /* XXX dynamic */
185: packet_put_char(0); /* XXX partial success, unused */
186: packet_send();
187: packet_write_wait();
188: } else {
189: log("userauth postponed for %s method %s", user, method);
190: }
191: xfree(service);
192: xfree(user);
193: xfree(method);
194: }
195:
196: int
197: ssh2_auth_none(struct passwd *pw)
198: {
199: packet_done();
200: return auth_password(pw, "");
201: }
202: int
203: ssh2_auth_password(struct passwd *pw)
204: {
205: char *password;
206: int authenticated = 0;
207: int change;
208: unsigned int len;
209: change = packet_get_char();
210: if (change)
211: log("password change not supported");
212: password = packet_get_string(&len);
213: packet_done();
214: if (auth_password(pw, password))
215: authenticated = 1;
216: memset(password, 0, len);
217: xfree(password);
218: return authenticated;
219: }
220:
221: int
222: ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen)
223: {
224: Buffer b;
225: Key *key;
226: char *pkalg, *pkblob, *sig;
227: unsigned int alen, blen, slen;
228: int have_sig;
229: int authenticated = 0;
230:
231: have_sig = packet_get_char();
232: pkalg = packet_get_string(&alen);
233: if (strcmp(pkalg, KEX_DSS) != 0) {
234: xfree(pkalg);
235: log("bad pkalg %s", pkalg); /*XXX*/
236: return 0;
237: }
238: pkblob = packet_get_string(&blen);
239: key = dsa_key_from_blob(pkblob, blen);
1.2 ! markus 240: if (key != NULL) {
! 241: if (have_sig) {
! 242: sig = packet_get_string(&slen);
! 243: packet_done();
! 244: buffer_init(&b);
! 245: buffer_append(&b, session_id2, session_id2_len);
! 246: buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
! 247: if (slen + 4 > rlen)
! 248: fatal("bad rlen/slen");
! 249: buffer_append(&b, raw, rlen - slen - 4);
1.1 markus 250: #ifdef DEBUG_DSS
1.2 ! markus 251: buffer_dump(&b);
1.1 markus 252: #endif
1.2 ! markus 253: /* test for correct signature */
! 254: if (user_dsa_key_allowed(pw, key) &&
! 255: dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
! 256: authenticated = 1;
! 257: buffer_clear(&b);
! 258: xfree(sig);
! 259: } else {
! 260: packet_done();
! 261: debug("test key...");
! 262: /* test whether pkalg/pkblob are acceptable */
! 263: /* XXX fake reply and always send PK_OK ? */
! 264: if (user_dsa_key_allowed(pw, key)) {
! 265: packet_start(SSH2_MSG_USERAUTH_PK_OK);
! 266: packet_put_string(pkalg, alen);
! 267: packet_put_string(pkblob, blen);
! 268: packet_send();
! 269: packet_write_wait();
! 270: authenticated = -1;
! 271: }
1.1 markus 272: }
1.2 ! markus 273: key_free(key);
1.1 markus 274: }
275: xfree(pkalg);
276: xfree(pkblob);
277: return authenticated;
278: }
279:
280: /* set and get current user */
281:
282: struct passwd*
283: auth_get_user(void)
284: {
285: return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
286: }
287:
288: struct passwd*
289: auth_set_user(char *u, char *s)
290: {
291: struct passwd *pw, *copy;
292:
293: if (authctxt == NULL) {
294: authctxt = xmalloc(sizeof(*authctxt));
295: authctxt->valid = 0;
296: authctxt->user = xstrdup(u);
297: authctxt->service = xstrdup(s);
298: setproctitle("%s", u);
299: pw = getpwnam(u);
300: if (!pw || !allowed_user(pw)) {
301: log("auth_set_user: bad user %s", u);
302: return NULL;
303: }
304: copy = &authctxt->pw;
305: memset(copy, 0, sizeof(*copy));
306: copy->pw_name = xstrdup(pw->pw_name);
307: copy->pw_passwd = xstrdup(pw->pw_passwd);
308: copy->pw_uid = pw->pw_uid;
309: copy->pw_gid = pw->pw_gid;
310: copy->pw_dir = xstrdup(pw->pw_dir);
311: copy->pw_shell = xstrdup(pw->pw_shell);
312: authctxt->valid = 1;
313: } else {
314: if (strcmp(u, authctxt->user) != 0 ||
315: strcmp(s, authctxt->service) != 0) {
316: log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
317: u, s, authctxt->user, authctxt->service);
318: return NULL;
319: }
320: }
321: return auth_get_user();
322: }
323:
324: /* return 1 if user allows given key */
325: int
326: user_dsa_key_allowed(struct passwd *pw, Key *key)
327: {
328: char line[8192], file[1024];
329: int found_key = 0;
330: unsigned int bits = -1;
331: FILE *f;
332: unsigned long linenum = 0;
333: struct stat st;
334: Key *found;
335:
336: /* Temporarily use the user's uid. */
337: temporarily_use_uid(pw->pw_uid);
338:
339: /* The authorized keys. */
340: snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
341: SSH_USER_PERMITTED_KEYS2);
342:
343: /* Fail quietly if file does not exist */
344: if (stat(file, &st) < 0) {
345: /* Restore the privileged uid. */
346: restore_uid();
347: return 0;
348: }
349: /* Open the file containing the authorized keys. */
350: f = fopen(file, "r");
351: if (!f) {
352: /* Restore the privileged uid. */
353: restore_uid();
354: packet_send_debug("Could not open %.900s for reading.", file);
355: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
356: return 0;
357: }
358: if (options.strict_modes) {
359: int fail = 0;
360: char buf[1024];
361: /* Check open file in order to avoid open/stat races */
362: if (fstat(fileno(f), &st) < 0 ||
363: (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
364: (st.st_mode & 022) != 0) {
365: snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
366: "bad ownership or modes for '%s'.", pw->pw_name, file);
367: fail = 1;
368: } else {
369: /* Check path to SSH_USER_PERMITTED_KEYS */
370: int i;
371: static const char *check[] = {
372: "", SSH_USER_DIR, NULL
373: };
374: for (i = 0; check[i]; i++) {
375: snprintf(line, sizeof line, "%.500s/%.100s",
376: pw->pw_dir, check[i]);
377: if (stat(line, &st) < 0 ||
378: (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
379: (st.st_mode & 022) != 0) {
380: snprintf(buf, sizeof buf,
381: "DSA authentication refused for %.100s: "
382: "bad ownership or modes for '%s'.",
383: pw->pw_name, line);
384: fail = 1;
385: break;
386: }
387: }
388: }
389: if (fail) {
390: log(buf);
391: fclose(f);
392: restore_uid();
393: return 0;
394: }
395: }
396: found_key = 0;
397: found = key_new(KEY_DSA);
398:
399: while (fgets(line, sizeof(line), f)) {
400: char *cp;
401: linenum++;
402: /* Skip leading whitespace, empty and comment lines. */
403: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
404: ;
405: if (!*cp || *cp == '\n' || *cp == '#')
406: continue;
407: bits = key_read(found, &cp);
408: if (bits == 0)
409: continue;
410: if (key_equal(found, key)) {
411: found_key = 1;
412: debug("matching key found: file %s, line %ld",
413: file, linenum);
414: break;
415: }
416: }
417: restore_uid();
418: fclose(f);
419: key_free(found);
420: return found_key;
421: }