Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.1
1.1 ! provos 1: /*
! 2:
! 3: auth-rsa.c
! 4:
! 5: Author: Tatu Ylonen <ylo@cs.hut.fi>
! 6:
! 7: Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
! 8: All rights reserved
! 9:
! 10: Created: Mon Mar 27 01:46:52 1995 ylo
! 11:
! 12: RSA-based authentication. This code determines whether to admit a login
! 13: based on RSA authentication. This file also contains functions to check
! 14: validity of the host key.
! 15:
! 16: */
! 17:
! 18: #include "includes.h"
! 19: RCSID("$Id: auth-rsa.c,v 1.4 1999/06/14 14:41:35 bg Exp $");
! 20:
! 21: #include "rsa.h"
! 22: #include "packet.h"
! 23: #include "xmalloc.h"
! 24: #include "ssh.h"
! 25: #include "ssh_md5.h"
! 26: #include "mpaux.h"
! 27: #include "uidswap.h"
! 28:
! 29: #include <ssl/rsa.h>
! 30:
! 31: /* Flags that may be set in authorized_keys options. */
! 32: extern int no_port_forwarding_flag;
! 33: extern int no_agent_forwarding_flag;
! 34: extern int no_x11_forwarding_flag;
! 35: extern int no_pty_flag;
! 36: extern char *forced_command;
! 37: extern struct envstring *custom_environment;
! 38:
! 39: /* Session identifier that is used to bind key exchange and authentication
! 40: responses to a particular session. */
! 41: extern unsigned char session_id[16];
! 42:
! 43: /* The .ssh/authorized_keys file contains public keys, one per line, in the
! 44: following format:
! 45: options bits e n comment
! 46: where bits, e and n are decimal numbers,
! 47: and comment is any string of characters up to newline. The maximum
! 48: length of a line is 8000 characters. See the documentation for a
! 49: description of the options.
! 50: */
! 51:
! 52: /* Performs the RSA authentication challenge-response dialog with the client,
! 53: and returns true (non-zero) if the client gave the correct answer to
! 54: our challenge; returns zero if the client gives a wrong answer. */
! 55:
! 56: int
! 57: auth_rsa_challenge_dialog(unsigned int bits, BIGNUM *e, BIGNUM *n)
! 58: {
! 59: BIGNUM *challenge, *encrypted_challenge, *aux;
! 60: RSA *pk;
! 61: BN_CTX *ctx = BN_CTX_new();
! 62: unsigned char buf[32], mdbuf[16], response[16];
! 63: struct MD5Context md;
! 64: unsigned int i;
! 65: int plen, len;
! 66:
! 67: encrypted_challenge = BN_new();
! 68: challenge = BN_new();
! 69: aux = BN_new();
! 70:
! 71: /* Generate a random challenge. */
! 72: BN_rand(challenge, 256, 0, 0);
! 73: BN_mod(challenge, challenge, n, ctx);
! 74:
! 75: /* Create the public key data structure. */
! 76: pk = RSA_new();
! 77: pk->e = BN_new();
! 78: BN_copy(pk->e, e);
! 79: pk->n = BN_new();
! 80: BN_copy(pk->n, n);
! 81:
! 82: /* Encrypt the challenge with the public key. */
! 83: rsa_public_encrypt(encrypted_challenge, challenge, pk);
! 84: RSA_free(pk);
! 85:
! 86: /* Send the encrypted challenge to the client. */
! 87: packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
! 88: packet_put_bignum(encrypted_challenge);
! 89: packet_send();
! 90: packet_write_wait();
! 91:
! 92: /* The response is MD5 of decrypted challenge plus session id. */
! 93: len = (BN_num_bits(challenge) + 7) / 8;
! 94: assert(len <= 32 && len);
! 95: memset(buf, 0, 32);
! 96: BN_bn2bin(challenge, buf + 32 - len);
! 97: MD5Init(&md);
! 98: MD5Update(&md, buf, 32);
! 99: MD5Update(&md, session_id, 16);
! 100: MD5Final(mdbuf, &md);
! 101:
! 102: /* We will no longer need these. */
! 103: BN_clear_free(encrypted_challenge);
! 104: BN_clear_free(challenge);
! 105: BN_clear_free(aux);
! 106: BN_CTX_free(ctx);
! 107:
! 108: /* Wait for a response. */
! 109: packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE);
! 110: packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE);
! 111: for (i = 0; i < 16; i++)
! 112: response[i] = packet_get_char();
! 113:
! 114: /* Verify that the response is the original challenge. */
! 115: if (memcmp(response, mdbuf, 16) != 0)
! 116: {
! 117: /* Wrong answer. */
! 118: return 0;
! 119: }
! 120:
! 121: /* Correct answer. */
! 122: return 1;
! 123: }
! 124:
! 125: /* Performs the RSA authentication dialog with the client. This returns
! 126: 0 if the client could not be authenticated, and 1 if authentication was
! 127: successful. This may exit if there is a serious protocol violation. */
! 128:
! 129: int
! 130: auth_rsa(struct passwd *pw, BIGNUM *client_n)
! 131: {
! 132: char line[8192];
! 133: int authenticated;
! 134: unsigned int bits;
! 135: FILE *f;
! 136: unsigned long linenum = 0;
! 137: struct stat st;
! 138: BIGNUM *e, *n;
! 139:
! 140: /* Open the file containing the authorized keys. */
! 141: sprintf(line, "%.500s/%.100s", pw->pw_dir, SSH_USER_PERMITTED_KEYS);
! 142:
! 143: /* Temporarily use the user's uid. */
! 144: temporarily_use_uid(pw->pw_uid);
! 145: if (stat(line, &st) < 0)
! 146: {
! 147: /* Restore the privileged uid. */
! 148: restore_uid();
! 149: return 0;
! 150: }
! 151: f = fopen(line, "r");
! 152: if (!f)
! 153: {
! 154: /* Restore the privileged uid. */
! 155: restore_uid();
! 156: packet_send_debug("Could not open %.900s for reading.", line);
! 157: packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
! 158: return 0;
! 159: }
! 160:
! 161: /* Flag indicating whether authentication has succeeded. */
! 162: authenticated = 0;
! 163:
! 164: /* Initialize mp-int variables. */
! 165: e = BN_new();
! 166: n = BN_new();
! 167:
! 168: /* Go though the accepted keys, looking for the current key. If found,
! 169: perform a challenge-response dialog to verify that the user really has
! 170: the corresponding private key. */
! 171: while (fgets(line, sizeof(line), f))
! 172: {
! 173: char *cp;
! 174: char *options;
! 175:
! 176: linenum++;
! 177:
! 178: /* Skip leading whitespace. */
! 179: for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
! 180: ;
! 181:
! 182: /* Skip empty and comment lines. */
! 183: if (!*cp || *cp == '\n' || *cp == '#')
! 184: continue;
! 185:
! 186: /* Check if there are options for this key, and if so, save their
! 187: starting address and skip the option part for now. If there are no
! 188: options, set the starting address to NULL. */
! 189: if (*cp < '0' || *cp > '9')
! 190: {
! 191: int quoted = 0;
! 192: options = cp;
! 193: for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
! 194: {
! 195: if (*cp == '\\' && cp[1] == '"')
! 196: cp++; /* Skip both */
! 197: else
! 198: if (*cp == '"')
! 199: quoted = !quoted;
! 200: }
! 201: }
! 202: else
! 203: options = NULL;
! 204:
! 205: /* Parse the key from the line. */
! 206: if (!auth_rsa_read_key(&cp, &bits, e, n))
! 207: {
! 208: debug("%.100s, line %lu: bad key syntax",
! 209: SSH_USER_PERMITTED_KEYS, linenum);
! 210: packet_send_debug("%.100s, line %lu: bad key syntax",
! 211: SSH_USER_PERMITTED_KEYS, linenum);
! 212: continue;
! 213: }
! 214: /* cp now points to the comment part. */
! 215:
! 216: /* Check if the we have found the desired key (identified by its
! 217: modulus). */
! 218: if (BN_cmp(n, client_n) != 0)
! 219: continue; /* Wrong key. */
! 220:
! 221: /* We have found the desired key. */
! 222:
! 223: /* Perform the challenge-response dialog for this key. */
! 224: if (!auth_rsa_challenge_dialog(bits, e, n))
! 225: {
! 226: /* Wrong response. */
! 227: log("Wrong response to RSA authentication challenge.");
! 228: packet_send_debug("Wrong response to RSA authentication challenge.");
! 229: continue;
! 230: }
! 231:
! 232: /* Correct response. The client has been successfully authenticated.
! 233: Note that we have not yet processed the options; this will be reset
! 234: if the options cause the authentication to be rejected. */
! 235: authenticated = 1;
! 236:
! 237: /* RSA part of authentication was accepted. Now process the options. */
! 238: if (options)
! 239: {
! 240: while (*options && *options != ' ' && *options != '\t')
! 241: {
! 242: cp = "no-port-forwarding";
! 243: if (strncmp(options, cp, strlen(cp)) == 0)
! 244: {
! 245: packet_send_debug("Port forwarding disabled.");
! 246: no_port_forwarding_flag = 1;
! 247: options += strlen(cp);
! 248: goto next_option;
! 249: }
! 250: cp = "no-agent-forwarding";
! 251: if (strncmp(options, cp, strlen(cp)) == 0)
! 252: {
! 253: packet_send_debug("Agent forwarding disabled.");
! 254: no_agent_forwarding_flag = 1;
! 255: options += strlen(cp);
! 256: goto next_option;
! 257: }
! 258: cp = "no-X11-forwarding";
! 259: if (strncmp(options, cp, strlen(cp)) == 0)
! 260: {
! 261: packet_send_debug("X11 forwarding disabled.");
! 262: no_x11_forwarding_flag = 1;
! 263: options += strlen(cp);
! 264: goto next_option;
! 265: }
! 266: cp = "no-pty";
! 267: if (strncmp(options, cp, strlen(cp)) == 0)
! 268: {
! 269: packet_send_debug("Pty allocation disabled.");
! 270: no_pty_flag = 1;
! 271: options += strlen(cp);
! 272: goto next_option;
! 273: }
! 274: cp = "command=\"";
! 275: if (strncmp(options, cp, strlen(cp)) == 0)
! 276: {
! 277: int i;
! 278: options += strlen(cp);
! 279: forced_command = xmalloc(strlen(options) + 1);
! 280: i = 0;
! 281: while (*options)
! 282: {
! 283: if (*options == '"')
! 284: break;
! 285: if (*options == '\\' && options[1] == '"')
! 286: {
! 287: options += 2;
! 288: forced_command[i++] = '"';
! 289: continue;
! 290: }
! 291: forced_command[i++] = *options++;
! 292: }
! 293: if (!*options)
! 294: {
! 295: debug("%.100s, line %lu: missing end quote",
! 296: SSH_USER_PERMITTED_KEYS, linenum);
! 297: packet_send_debug("%.100s, line %lu: missing end quote",
! 298: SSH_USER_PERMITTED_KEYS, linenum);
! 299: continue;
! 300: }
! 301: forced_command[i] = 0;
! 302: packet_send_debug("Forced command: %.900s", forced_command);
! 303: options++;
! 304: goto next_option;
! 305: }
! 306: cp = "environment=\"";
! 307: if (strncmp(options, cp, strlen(cp)) == 0)
! 308: {
! 309: int i;
! 310: char *s;
! 311: struct envstring *new_envstring;
! 312: options += strlen(cp);
! 313: s = xmalloc(strlen(options) + 1);
! 314: i = 0;
! 315: while (*options)
! 316: {
! 317: if (*options == '"')
! 318: break;
! 319: if (*options == '\\' && options[1] == '"')
! 320: {
! 321: options += 2;
! 322: s[i++] = '"';
! 323: continue;
! 324: }
! 325: s[i++] = *options++;
! 326: }
! 327: if (!*options)
! 328: {
! 329: debug("%.100s, line %lu: missing end quote",
! 330: SSH_USER_PERMITTED_KEYS, linenum);
! 331: packet_send_debug("%.100s, line %lu: missing end quote",
! 332: SSH_USER_PERMITTED_KEYS, linenum);
! 333: continue;
! 334: }
! 335: s[i] = 0;
! 336: packet_send_debug("Adding to environment: %.900s", s);
! 337: debug("Adding to environment: %.900s", s);
! 338: options++;
! 339: new_envstring = xmalloc(sizeof(struct envstring));
! 340: new_envstring->s = s;
! 341: new_envstring->next = custom_environment;
! 342: custom_environment = new_envstring;
! 343: goto next_option;
! 344: }
! 345: cp = "from=\"";
! 346: if (strncmp(options, cp, strlen(cp)) == 0)
! 347: {
! 348: char *patterns = xmalloc(strlen(options) + 1);
! 349: int i;
! 350: options += strlen(cp);
! 351: i = 0;
! 352: while (*options)
! 353: {
! 354: if (*options == '"')
! 355: break;
! 356: if (*options == '\\' && options[1] == '"')
! 357: {
! 358: options += 2;
! 359: patterns[i++] = '"';
! 360: continue;
! 361: }
! 362: patterns[i++] = *options++;
! 363: }
! 364: if (!*options)
! 365: {
! 366: debug("%.100s, line %lu: missing end quote",
! 367: SSH_USER_PERMITTED_KEYS, linenum);
! 368: packet_send_debug("%.100s, line %lu: missing end quote",
! 369: SSH_USER_PERMITTED_KEYS, linenum);
! 370: continue;
! 371: }
! 372: patterns[i] = 0;
! 373: options++;
! 374: if (!match_hostname(get_canonical_hostname(), patterns,
! 375: strlen(patterns)) &&
! 376: !match_hostname(get_remote_ipaddr(), patterns,
! 377: strlen(patterns)))
! 378: {
! 379: log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
! 380: pw->pw_name, get_canonical_hostname(),
! 381: get_remote_ipaddr());
! 382: packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
! 383: get_canonical_hostname());
! 384: xfree(patterns);
! 385: authenticated = 0;
! 386: break;
! 387: }
! 388: xfree(patterns);
! 389: /* Host name matches. */
! 390: goto next_option;
! 391: }
! 392: bad_option:
! 393: /* Unknown option. */
! 394: log("Bad options in %.100s file, line %lu: %.50s",
! 395: SSH_USER_PERMITTED_KEYS, linenum, options);
! 396: packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
! 397: SSH_USER_PERMITTED_KEYS, linenum, options);
! 398: authenticated = 0;
! 399: break;
! 400:
! 401: next_option:
! 402: /* Skip the comma, and move to the next option (or break out
! 403: if there are no more). */
! 404: if (!*options)
! 405: fatal("Bugs in auth-rsa.c option processing.");
! 406: if (*options == ' ' || *options == '\t')
! 407: break; /* End of options. */
! 408: if (*options != ',')
! 409: goto bad_option;
! 410: options++;
! 411: /* Process the next option. */
! 412: continue;
! 413: }
! 414: }
! 415:
! 416: /* Break out of the loop if authentication was successful; otherwise
! 417: continue searching. */
! 418: if (authenticated)
! 419: break;
! 420: }
! 421:
! 422: /* Restore the privileged uid. */
! 423: restore_uid();
! 424:
! 425: /* Close the file. */
! 426: fclose(f);
! 427:
! 428: /* Clear any mp-int variables. */
! 429: BN_clear_free(n);
! 430: BN_clear_free(e);
! 431:
! 432: if (authenticated)
! 433: packet_send_debug("RSA authentication accepted.");
! 434:
! 435: /* Return authentication result. */
! 436: return authenticated;
! 437: }