Annotation of src/usr.bin/ssh/ssh-agent.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2:
! 3: ssh-agent.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: Wed Mar 29 03:46:59 1995 ylo
! 11:
! 12: The authentication agent program.
! 13:
! 14: */
! 15:
! 16: #include "includes.h"
! 17: RCSID("$Id: ssh-agent.c,v 1.3 1999/05/04 11:59:14 bg Exp $");
! 18:
! 19: #include "ssh.h"
! 20: #include "rsa.h"
! 21: #include "randoms.h"
! 22: #include "authfd.h"
! 23: #include "buffer.h"
! 24: #include "bufaux.h"
! 25: #include "xmalloc.h"
! 26: #include "packet.h"
! 27: #include "ssh_md5.h"
! 28: #include "getput.h"
! 29: #include "mpaux.h"
! 30:
! 31: typedef struct
! 32: {
! 33: int fd;
! 34: enum { AUTH_UNUSED, AUTH_FD, AUTH_SOCKET, AUTH_SOCKET_FD,
! 35: AUTH_CONNECTION } type;
! 36: Buffer input;
! 37: Buffer output;
! 38: } SocketEntry;
! 39:
! 40: unsigned int sockets_alloc = 0;
! 41: SocketEntry *sockets = NULL;
! 42:
! 43: typedef struct
! 44: {
! 45: RSAPrivateKey key;
! 46: char *comment;
! 47: } Identity;
! 48:
! 49: unsigned int num_identities = 0;
! 50: Identity *identities = NULL;
! 51:
! 52: int max_fd = 0;
! 53:
! 54: void process_request_identity(SocketEntry *e)
! 55: {
! 56: Buffer msg;
! 57: int i;
! 58:
! 59: buffer_init(&msg);
! 60: buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
! 61: buffer_put_int(&msg, num_identities);
! 62: for (i = 0; i < num_identities; i++)
! 63: {
! 64: buffer_put_int(&msg, identities[i].key.bits);
! 65: buffer_put_mp_int(&msg, &identities[i].key.e);
! 66: buffer_put_mp_int(&msg, &identities[i].key.n);
! 67: buffer_put_string(&msg, identities[i].comment,
! 68: strlen(identities[i].comment));
! 69: }
! 70: buffer_put_int(&e->output, buffer_len(&msg));
! 71: buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
! 72: buffer_free(&msg);
! 73: }
! 74:
! 75: void process_authentication_challenge(SocketEntry *e)
! 76: {
! 77: int i, pub_bits;
! 78: MP_INT pub_e, pub_n, challenge;
! 79: Buffer msg;
! 80: struct MD5Context md;
! 81: unsigned char buf[32], mdbuf[16], session_id[16];
! 82: unsigned int response_type;
! 83:
! 84: buffer_init(&msg);
! 85: mpz_init(&pub_e);
! 86: mpz_init(&pub_n);
! 87: mpz_init(&challenge);
! 88: pub_bits = buffer_get_int(&e->input);
! 89: buffer_get_mp_int(&e->input, &pub_e);
! 90: buffer_get_mp_int(&e->input, &pub_n);
! 91: buffer_get_mp_int(&e->input, &challenge);
! 92: if (buffer_len(&e->input) == 0)
! 93: {
! 94: /* Compatibility code for old servers. */
! 95: memset(session_id, 0, 16);
! 96: response_type = 0;
! 97: }
! 98: else
! 99: {
! 100: /* New code. */
! 101: buffer_get(&e->input, (char *)session_id, 16);
! 102: response_type = buffer_get_int(&e->input);
! 103: }
! 104: for (i = 0; i < num_identities; i++)
! 105: if (pub_bits == identities[i].key.bits &&
! 106: mpz_cmp(&pub_e, &identities[i].key.e) == 0 &&
! 107: mpz_cmp(&pub_n, &identities[i].key.n) == 0)
! 108: {
! 109: /* Decrypt the challenge using the private key. */
! 110: rsa_private_decrypt(&challenge, &challenge, &identities[i].key);
! 111:
! 112: /* Compute the desired response. */
! 113: switch (response_type)
! 114: {
! 115: case 0: /* As of protocol 1.0 */
! 116: /* This response type is no longer supported. */
! 117: log("Compatibility with ssh protocol 1.0 no longer supported.");
! 118: buffer_put_char(&msg, SSH_AGENT_FAILURE);
! 119: goto send;
! 120:
! 121: case 1: /* As of protocol 1.1 */
! 122: /* The response is MD5 of decrypted challenge plus session id. */
! 123: mp_linearize_msb_first(buf, 32, &challenge);
! 124: MD5Init(&md);
! 125: MD5Update(&md, buf, 32);
! 126: MD5Update(&md, session_id, 16);
! 127: MD5Final(mdbuf, &md);
! 128: break;
! 129:
! 130: default:
! 131: fatal("process_authentication_challenge: bad response_type %d",
! 132: response_type);
! 133: break;
! 134: }
! 135:
! 136: /* Send the response. */
! 137: buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
! 138: for (i = 0; i < 16; i++)
! 139: buffer_put_char(&msg, mdbuf[i]);
! 140:
! 141: goto send;
! 142: }
! 143: /* Unknown identity. Send failure. */
! 144: buffer_put_char(&msg, SSH_AGENT_FAILURE);
! 145: send:
! 146: buffer_put_int(&e->output, buffer_len(&msg));
! 147: buffer_append(&e->output, buffer_ptr(&msg),
! 148: buffer_len(&msg));
! 149: buffer_free(&msg);
! 150: mpz_clear(&pub_e);
! 151: mpz_clear(&pub_n);
! 152: mpz_clear(&challenge);
! 153: }
! 154:
! 155: void process_remove_identity(SocketEntry *e)
! 156: {
! 157: unsigned int bits;
! 158: MP_INT dummy, n;
! 159: unsigned int i;
! 160:
! 161: mpz_init(&dummy);
! 162: mpz_init(&n);
! 163:
! 164: /* Get the key from the packet. */
! 165: bits = buffer_get_int(&e->input);
! 166: buffer_get_mp_int(&e->input, &dummy);
! 167: buffer_get_mp_int(&e->input, &n);
! 168:
! 169: /* Check if we have the key. */
! 170: for (i = 0; i < num_identities; i++)
! 171: if (mpz_cmp(&identities[i].key.n, &n) == 0)
! 172: {
! 173: /* We have this key. Free the old key. Since we don\'t want to leave
! 174: empty slots in the middle of the array, we actually free the
! 175: key there and copy data from the last entry. */
! 176: rsa_clear_private_key(&identities[i].key);
! 177: xfree(identities[i].comment);
! 178: if (i < num_identities - 1)
! 179: identities[i] = identities[num_identities - 1];
! 180: num_identities--;
! 181: mpz_clear(&dummy);
! 182: mpz_clear(&n);
! 183:
! 184: /* Send success. */
! 185: buffer_put_int(&e->output, 1);
! 186: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
! 187: return;
! 188: }
! 189: /* We did not have the key. */
! 190: mpz_clear(&dummy);
! 191: mpz_clear(&n);
! 192:
! 193: /* Send failure. */
! 194: buffer_put_int(&e->output, 1);
! 195: buffer_put_char(&e->output, SSH_AGENT_FAILURE);
! 196: }
! 197:
! 198: /* Removes all identities from the agent. */
! 199:
! 200: void process_remove_all_identities(SocketEntry *e)
! 201: {
! 202: unsigned int i;
! 203:
! 204: /* Loop over all identities and clear the keys. */
! 205: for (i = 0; i < num_identities; i++)
! 206: {
! 207: rsa_clear_private_key(&identities[i].key);
! 208: xfree(identities[i].comment);
! 209: }
! 210:
! 211: /* Mark that there are no identities. */
! 212: num_identities = 0;
! 213:
! 214: /* Send success. */
! 215: buffer_put_int(&e->output, 1);
! 216: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
! 217: return;
! 218: }
! 219:
! 220: /* Adds an identity to the agent. */
! 221:
! 222: void process_add_identity(SocketEntry *e)
! 223: {
! 224: RSAPrivateKey *k;
! 225: int i;
! 226:
! 227: if (num_identities == 0)
! 228: identities = xmalloc(sizeof(Identity));
! 229: else
! 230: identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
! 231: k = &identities[num_identities].key;
! 232: k->bits = buffer_get_int(&e->input);
! 233: mpz_init(&k->n);
! 234: buffer_get_mp_int(&e->input, &k->n);
! 235: mpz_init(&k->e);
! 236: buffer_get_mp_int(&e->input, &k->e);
! 237: mpz_init(&k->d);
! 238: buffer_get_mp_int(&e->input, &k->d);
! 239: mpz_init(&k->u);
! 240: buffer_get_mp_int(&e->input, &k->u);
! 241: mpz_init(&k->p);
! 242: buffer_get_mp_int(&e->input, &k->p);
! 243: mpz_init(&k->q);
! 244: buffer_get_mp_int(&e->input, &k->q);
! 245: identities[num_identities].comment = buffer_get_string(&e->input, NULL);
! 246:
! 247: /* Check if we already have the key. */
! 248: for (i = 0; i < num_identities; i++)
! 249: if (mpz_cmp(&identities[i].key.n, &k->n) == 0)
! 250: {
! 251: /* We already have this key. Clear and free the new data and
! 252: return success. */
! 253: rsa_clear_private_key(k);
! 254: xfree(identities[num_identities].comment);
! 255:
! 256: /* Send success. */
! 257: buffer_put_int(&e->output, 1);
! 258: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
! 259: return;
! 260: }
! 261:
! 262: /* Increment the number of identities. */
! 263: num_identities++;
! 264:
! 265: /* Send a success message. */
! 266: buffer_put_int(&e->output, 1);
! 267: buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
! 268: }
! 269:
! 270: void process_message(SocketEntry *e)
! 271: {
! 272: unsigned int msg_len;
! 273: unsigned int type;
! 274: unsigned char *cp;
! 275: if (buffer_len(&e->input) < 5)
! 276: return; /* Incomplete message. */
! 277: cp = (unsigned char *)buffer_ptr(&e->input);
! 278: msg_len = GET_32BIT(cp);
! 279: if (msg_len > 256 * 1024)
! 280: {
! 281: shutdown(e->fd, 2);
! 282: close(e->fd);
! 283: e->type = AUTH_UNUSED;
! 284: return;
! 285: }
! 286: if (buffer_len(&e->input) < msg_len + 4)
! 287: return;
! 288: buffer_consume(&e->input, 4);
! 289: type = buffer_get_char(&e->input);
! 290: switch (type)
! 291: {
! 292: case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
! 293: process_request_identity(e);
! 294: break;
! 295: case SSH_AGENTC_RSA_CHALLENGE:
! 296: process_authentication_challenge(e);
! 297: break;
! 298: case SSH_AGENTC_ADD_RSA_IDENTITY:
! 299: process_add_identity(e);
! 300: break;
! 301: case SSH_AGENTC_REMOVE_RSA_IDENTITY:
! 302: process_remove_identity(e);
! 303: break;
! 304: case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
! 305: process_remove_all_identities(e);
! 306: break;
! 307: default:
! 308: /* Unknown message. Respond with failure. */
! 309: error("Unknown message %d", type);
! 310: buffer_clear(&e->input);
! 311: buffer_put_int(&e->output, 1);
! 312: buffer_put_char(&e->output, SSH_AGENT_FAILURE);
! 313: break;
! 314: }
! 315: }
! 316:
! 317: void new_socket(int type, int fd)
! 318: {
! 319: unsigned int i, old_alloc;
! 320: #if defined(O_NONBLOCK) && !defined(O_NONBLOCK_BROKEN)
! 321: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
! 322: error("fcntl O_NONBLOCK: %s", strerror(errno));
! 323: #else /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
! 324: if (fcntl(fd, F_SETFL, O_NDELAY) < 0)
! 325: error("fcntl O_NDELAY: %s", strerror(errno));
! 326: #endif /* O_NONBLOCK && !O_NONBLOCK_BROKEN */
! 327:
! 328: if (fd > max_fd)
! 329: max_fd = fd;
! 330:
! 331: for (i = 0; i < sockets_alloc; i++)
! 332: if (sockets[i].type == AUTH_UNUSED)
! 333: {
! 334: sockets[i].fd = fd;
! 335: sockets[i].type = type;
! 336: buffer_init(&sockets[i].input);
! 337: buffer_init(&sockets[i].output);
! 338: return;
! 339: }
! 340: old_alloc = sockets_alloc;
! 341: sockets_alloc += 10;
! 342: if (sockets)
! 343: sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
! 344: else
! 345: sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
! 346: for (i = old_alloc; i < sockets_alloc; i++)
! 347: sockets[i].type = AUTH_UNUSED;
! 348: sockets[old_alloc].type = type;
! 349: sockets[old_alloc].fd = fd;
! 350: buffer_init(&sockets[old_alloc].input);
! 351: buffer_init(&sockets[old_alloc].output);
! 352: }
! 353:
! 354: void prepare_select(fd_set *readset, fd_set *writeset)
! 355: {
! 356: unsigned int i;
! 357: for (i = 0; i < sockets_alloc; i++)
! 358: switch (sockets[i].type)
! 359: {
! 360: case AUTH_FD:
! 361: case AUTH_CONNECTION:
! 362: case AUTH_SOCKET:
! 363: case AUTH_SOCKET_FD:
! 364: FD_SET(sockets[i].fd, readset);
! 365: if (buffer_len(&sockets[i].output) > 0)
! 366: FD_SET(sockets[i].fd, writeset);
! 367: break;
! 368: case AUTH_UNUSED:
! 369: break;
! 370: default:
! 371: fatal("Unknown socket type %d", sockets[i].type);
! 372: break;
! 373: }
! 374: }
! 375:
! 376: void after_select(fd_set *readset, fd_set *writeset)
! 377: {
! 378: unsigned int i;
! 379: int len, sock, port;
! 380: char buf[1024];
! 381: struct sockaddr_in sin;
! 382: struct sockaddr_un sunaddr;
! 383:
! 384: for (i = 0; i < sockets_alloc; i++)
! 385: switch (sockets[i].type)
! 386: {
! 387: case AUTH_UNUSED:
! 388: break;
! 389: case AUTH_FD:
! 390: if (FD_ISSET(sockets[i].fd, readset))
! 391: {
! 392: len = recv(sockets[i].fd, buf, sizeof(buf), 0);
! 393: if (len <= 0)
! 394: { /* All instances of the other side have been closed. */
! 395: log("Authentication agent exiting.");
! 396: exit(0);
! 397: }
! 398: process_auth_fd_input:
! 399: if (len != 3 || (unsigned char)buf[0] != SSH_AUTHFD_CONNECT)
! 400: break; /* Incorrect message; ignore it. */
! 401: /* It is a connection request message. */
! 402: port = (unsigned char)buf[1] * 256 + (unsigned char)buf[2];
! 403: memset(&sin, 0, sizeof(sin));
! 404: sin.sin_family = AF_INET;
! 405: sin.sin_addr.s_addr = htonl(0x7f000001); /* localhost */
! 406: sin.sin_port = htons(port);
! 407: sock = socket(AF_INET, SOCK_STREAM, 0);
! 408: if (sock < 0)
! 409: {
! 410: perror("socket");
! 411: break;
! 412: }
! 413: if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
! 414: {
! 415: perror("connecting to port requested in authfd message");
! 416: close(sock);
! 417: break;
! 418: }
! 419: new_socket(AUTH_CONNECTION, sock);
! 420: }
! 421: break;
! 422: case AUTH_SOCKET:
! 423: if (FD_ISSET(sockets[i].fd, readset))
! 424: {
! 425: len = sizeof(sunaddr);
! 426: sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
! 427: if (sock < 0)
! 428: {
! 429: perror("accept from AUTH_SOCKET");
! 430: break;
! 431: }
! 432: new_socket(AUTH_SOCKET_FD, sock);
! 433: }
! 434: break;
! 435: case AUTH_SOCKET_FD:
! 436: if (FD_ISSET(sockets[i].fd, readset))
! 437: {
! 438: len = recv(sockets[i].fd, buf, sizeof(buf), 0);
! 439: if (len <= 0)
! 440: { /* The other side has closed the socket. */
! 441: shutdown(sockets[i].fd, 2);
! 442: close(sockets[i].fd);
! 443: sockets[i].type = AUTH_UNUSED;
! 444: break;
! 445: }
! 446: goto process_auth_fd_input;
! 447: }
! 448: break;
! 449: case AUTH_CONNECTION:
! 450: if (buffer_len(&sockets[i].output) > 0 &&
! 451: FD_ISSET(sockets[i].fd, writeset))
! 452: {
! 453: len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
! 454: buffer_len(&sockets[i].output));
! 455: if (len <= 0)
! 456: {
! 457: shutdown(sockets[i].fd, 2);
! 458: close(sockets[i].fd);
! 459: sockets[i].type = AUTH_UNUSED;
! 460: break;
! 461: }
! 462: buffer_consume(&sockets[i].output, len);
! 463: }
! 464: if (FD_ISSET(sockets[i].fd, readset))
! 465: {
! 466: len = read(sockets[i].fd, buf, sizeof(buf));
! 467: if (len <= 0)
! 468: {
! 469: shutdown(sockets[i].fd, 2);
! 470: close(sockets[i].fd);
! 471: sockets[i].type = AUTH_UNUSED;
! 472: break;
! 473: }
! 474: buffer_append(&sockets[i].input, buf, len);
! 475: process_message(&sockets[i]);
! 476: }
! 477: break;
! 478: default:
! 479: fatal("Unknown type %d", sockets[i].type);
! 480: }
! 481: }
! 482:
! 483: int parent_pid = -1;
! 484: char socket_name[1024];
! 485:
! 486: RETSIGTYPE check_parent_exists(int sig)
! 487: {
! 488: if (kill(parent_pid, 0) < 0)
! 489: {
! 490: remove(socket_name);
! 491: /* printf("Parent has died - Authentication agent exiting.\n"); */
! 492: exit(1);
! 493: }
! 494: signal(SIGALRM, check_parent_exists);
! 495: alarm(10);
! 496: }
! 497:
! 498: int main(int ac, char **av)
! 499: {
! 500: fd_set readset, writeset;
! 501: char buf[1024];
! 502: int pfd;
! 503: int sock;
! 504: struct sockaddr_un sunaddr;
! 505:
! 506: int sockets[2], i;
! 507: int *dups;
! 508:
! 509: if (ac < 2)
! 510: {
! 511: fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
! 512: fprintf(stderr, "Usage: %s command\n", av[0]);
! 513: exit(1);
! 514: }
! 515:
! 516: pfd = get_permanent_fd(NULL);
! 517: if (pfd < 0)
! 518: {
! 519: /* The agent uses SSH_AUTHENTICATION_SOCKET. */
! 520:
! 521: parent_pid = getpid();
! 522:
! 523: sprintf(socket_name, SSH_AGENT_SOCKET, parent_pid);
! 524:
! 525: /* Fork, and have the parent execute the command. The child continues as
! 526: the authentication agent. */
! 527: if (fork() != 0)
! 528: { /* Parent - execute the given command. */
! 529: sprintf(buf, "SSH_AUTHENTICATION_SOCKET=%s", socket_name);
! 530: putenv(buf);
! 531: execvp(av[1], av + 1);
! 532: perror(av[1]);
! 533: exit(1);
! 534: }
! 535:
! 536: sock = socket(AF_UNIX, SOCK_STREAM, 0);
! 537: if (sock < 0)
! 538: {
! 539: perror("socket");
! 540: exit(1);
! 541: }
! 542: memset(&sunaddr, 0, sizeof(sunaddr));
! 543: sunaddr.sun_family = AF_UNIX;
! 544: strncpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
! 545: if (bind(sock, (struct sockaddr *)&sunaddr, AF_UNIX_SIZE(sunaddr)) < 0)
! 546: {
! 547: perror("bind");
! 548: exit(1);
! 549: }
! 550: if (chmod(socket_name, 0700) < 0)
! 551: {
! 552: perror("chmod");
! 553: exit(1);
! 554: }
! 555: if (listen(sock, 5) < 0)
! 556: {
! 557: perror("listen");
! 558: exit(1);
! 559: }
! 560: new_socket(AUTH_SOCKET, sock);
! 561: signal(SIGALRM, check_parent_exists);
! 562: alarm(10);
! 563: }
! 564: else
! 565: {
! 566: /* The agent uses SSH_AUTHENTICATION_FD. */
! 567: int cnt, newfd;
! 568:
! 569: dups = xmalloc(sizeof (int) * (1 + pfd));
! 570:
! 571: if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0)
! 572: {
! 573: perror("socketpair");
! 574: exit(1);
! 575: }
! 576:
! 577: /* Dup some descriptors to get the authentication fd to pfd,
! 578: because some shells arbitrarily close descriptors below that.
! 579: Don't use dup2 because maybe some systems don't have it?? */
! 580: for (cnt = 0;; cnt++) {
! 581: if ((dups[cnt] = dup(0)) < 0)
! 582: fatal("auth_input_request_forwarding: dup failed");
! 583: if (dups[cnt] == pfd)
! 584: break;
! 585: }
! 586: close(dups[cnt]);
! 587:
! 588: /* Move the file descriptor we pass to children up high where
! 589: the shell won't close it. */
! 590: newfd = dup(sockets[1]);
! 591: if (newfd != pfd)
! 592: fatal("auth_input_request_forwarding: dup didn't return %d.", pfd);
! 593: close(sockets[1]);
! 594: sockets[1] = newfd;
! 595: /* Close duped descriptors. */
! 596: for (i = 0; i < cnt; i++)
! 597: close(dups[i]);
! 598: free(dups);
! 599:
! 600: if (fork() != 0)
! 601: { /* Parent - execute the given command. */
! 602: close(sockets[0]);
! 603: sprintf(buf, "SSH_AUTHENTICATION_FD=%d", sockets[1]);
! 604: putenv(buf);
! 605: execvp(av[1], av + 1);
! 606: perror(av[1]);
! 607: exit(1);
! 608: }
! 609: close(sockets[1]);
! 610: new_socket(AUTH_FD, sockets[0]);
! 611: }
! 612:
! 613: signal(SIGINT, SIG_IGN);
! 614: while (1)
! 615: {
! 616: FD_ZERO(&readset);
! 617: FD_ZERO(&writeset);
! 618: prepare_select(&readset, &writeset);
! 619: if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
! 620: {
! 621: if (errno == EINTR)
! 622: continue;
! 623: perror("select");
! 624: exit(1);
! 625: }
! 626: after_select(&readset, &writeset);
! 627: }
! 628: /*NOTREACHED*/
! 629: }