Annotation of src/usr.bin/ssh/auth2.c, Revision 1.1
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"
! 30: RCSID("$OpenBSD: auth2.c,v 1.1 2000/04/18 15:01:30 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 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);
! 240:
! 241: if (have_sig && key != NULL) {
! 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);
! 250: #ifdef DEBUG_DSS
! 251: buffer_dump(&b);
! 252: #endif
! 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 if (!have_sig && key != NULL) {
! 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: }
! 272: }
! 273: xfree(pkalg);
! 274: xfree(pkblob);
! 275: return authenticated;
! 276: }
! 277:
! 278: /* set and get current user */
! 279:
! 280: struct passwd*
! 281: auth_get_user(void)
! 282: {
! 283: return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
! 284: }
! 285:
! 286: struct passwd*
! 287: auth_set_user(char *u, char *s)
! 288: {
! 289: struct passwd *pw, *copy;
! 290:
! 291: if (authctxt == NULL) {
! 292: authctxt = xmalloc(sizeof(*authctxt));
! 293: authctxt->valid = 0;
! 294: authctxt->user = xstrdup(u);
! 295: authctxt->service = xstrdup(s);
! 296: setproctitle("%s", u);
! 297: pw = getpwnam(u);
! 298: if (!pw || !allowed_user(pw)) {
! 299: log("auth_set_user: bad user %s", u);
! 300: return NULL;
! 301: }
! 302: copy = &authctxt->pw;
! 303: memset(copy, 0, sizeof(*copy));
! 304: copy->pw_name = xstrdup(pw->pw_name);
! 305: copy->pw_passwd = xstrdup(pw->pw_passwd);
! 306: copy->pw_uid = pw->pw_uid;
! 307: copy->pw_gid = pw->pw_gid;
! 308: copy->pw_dir = xstrdup(pw->pw_dir);
! 309: copy->pw_shell = xstrdup(pw->pw_shell);
! 310: authctxt->valid = 1;
! 311: } else {
! 312: if (strcmp(u, authctxt->user) != 0 ||
! 313: strcmp(s, authctxt->service) != 0) {
! 314: log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
! 315: u, s, authctxt->user, authctxt->service);
! 316: return NULL;
! 317: }
! 318: }
! 319: return auth_get_user();
! 320: }
! 321:
! 322: /* return 1 if user allows given key */
! 323: int
! 324: user_dsa_key_allowed(struct passwd *pw, Key *key)
! 325: {
! 326: char line[8192], file[1024];
! 327: int found_key = 0;
! 328: unsigned int bits = -1;
! 329: FILE *f;
! 330: unsigned long linenum = 0;
! 331: struct stat st;
! 332: Key *found;
! 333:
! 334: /* Temporarily use the user's uid. */
! 335: temporarily_use_uid(pw->pw_uid);
! 336:
! 337: /* The authorized keys. */
! 338: snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
! 339: SSH_USER_PERMITTED_KEYS2);
! 340:
! 341: /* Fail quietly if file does not exist */
! 342: if (stat(file, &st) < 0) {
! 343: /* Restore the privileged uid. */
! 344: restore_uid();
! 345: return 0;
! 346: }
! 347: /* Open the file containing the authorized keys. */
! 348: f = fopen(file, "r");
! 349: if (!f) {
! 350: /* Restore the privileged uid. */
! 351: restore_uid();
! 352: packet_send_debug("Could not open %.900s for reading.", file);
! 353: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
! 354: return 0;
! 355: }
! 356: if (options.strict_modes) {
! 357: int fail = 0;
! 358: char buf[1024];
! 359: /* Check open file in order to avoid open/stat races */
! 360: if (fstat(fileno(f), &st) < 0 ||
! 361: (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
! 362: (st.st_mode & 022) != 0) {
! 363: snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
! 364: "bad ownership or modes for '%s'.", pw->pw_name, file);
! 365: fail = 1;
! 366: } else {
! 367: /* Check path to SSH_USER_PERMITTED_KEYS */
! 368: int i;
! 369: static const char *check[] = {
! 370: "", SSH_USER_DIR, NULL
! 371: };
! 372: for (i = 0; check[i]; i++) {
! 373: snprintf(line, sizeof line, "%.500s/%.100s",
! 374: pw->pw_dir, check[i]);
! 375: if (stat(line, &st) < 0 ||
! 376: (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
! 377: (st.st_mode & 022) != 0) {
! 378: snprintf(buf, sizeof buf,
! 379: "DSA authentication refused for %.100s: "
! 380: "bad ownership or modes for '%s'.",
! 381: pw->pw_name, line);
! 382: fail = 1;
! 383: break;
! 384: }
! 385: }
! 386: }
! 387: if (fail) {
! 388: log(buf);
! 389: fclose(f);
! 390: restore_uid();
! 391: return 0;
! 392: }
! 393: }
! 394: found_key = 0;
! 395: found = key_new(KEY_DSA);
! 396:
! 397: while (fgets(line, sizeof(line), f)) {
! 398: char *cp;
! 399: linenum++;
! 400: /* Skip leading whitespace, empty and comment lines. */
! 401: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
! 402: ;
! 403: if (!*cp || *cp == '\n' || *cp == '#')
! 404: continue;
! 405: bits = key_read(found, &cp);
! 406: if (bits == 0)
! 407: continue;
! 408: if (key_equal(found, key)) {
! 409: found_key = 1;
! 410: debug("matching key found: file %s, line %ld",
! 411: file, linenum);
! 412: break;
! 413: }
! 414: }
! 415: restore_uid();
! 416: fclose(f);
! 417: key_free(found);
! 418: return found_key;
! 419: }