[BACK]Return to ssh-agent.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

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: }