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

Annotation of src/usr.bin/ssh/ssh-keyscan.c, Revision 1.1

1.1     ! markus      1: /*
        !             2:  * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
        !             3:  *
        !             4:  * Modification and redistribution in source and binary forms is
        !             5:  * permitted provided that due credit is given to the author and the
        !             6:  * OpenBSD project (for instance by leaving this copyright notice
        !             7:  * intact).
        !             8:  */
        !             9:
        !            10: #include "includes.h"
        !            11: RCSID("$OpenBSD:$");
        !            12:
        !            13: #include <sys/queue.h>
        !            14: #include <err.h>
        !            15: #include <errno.h>
        !            16:
        !            17: #include <ssl/bn.h>
        !            18: #include <ssl/rsa.h>
        !            19: #include <ssl/dsa.h>
        !            20:
        !            21: #include "xmalloc.h"
        !            22: #include "ssh.h"
        !            23: #include "key.h"
        !            24: #include "buffer.h"
        !            25: #include "bufaux.h"
        !            26:
        !            27: static int argno = 1;          /* Number of argument currently being parsed */
        !            28:
        !            29: int family = AF_UNSPEC;                /* IPv4, IPv6 or both */
        !            30:
        !            31: #define PORT 22
        !            32: #define MAXMAXFD 256
        !            33:
        !            34: /* The number of seconds after which to give up on a TCP connection */
        !            35: int timeout = 5;
        !            36:
        !            37: int maxfd;
        !            38: #define maxcon (maxfd - 10)
        !            39:
        !            40: char *prog;
        !            41: fd_set read_wait;
        !            42: int ncon;
        !            43:
        !            44: /*
        !            45:  * Keep a connection structure for each file descriptor.  The state
        !            46:  * associated with file descriptor n is held in fdcon[n].
        !            47:  */
        !            48: typedef struct Connection {
        !            49:        unsigned char c_status; /* State of connection on this file desc. */
        !            50: #define CS_UNUSED 0            /* File descriptor unused */
        !            51: #define CS_CON 1               /* Waiting to connect/read greeting */
        !            52: #define CS_SIZE 2              /* Waiting to read initial packet size */
        !            53: #define CS_KEYS 3              /* Waiting to read public key packet */
        !            54:        int c_fd;               /* Quick lookup: c->c_fd == c - fdcon */
        !            55:        int c_plen;             /* Packet length field for ssh packet */
        !            56:        int c_len;              /* Total bytes which must be read. */
        !            57:        int c_off;              /* Length of data read so far. */
        !            58:        char *c_namebase;       /* Address to free for c_name and c_namelist */
        !            59:        char *c_name;           /* Hostname of connection for errors */
        !            60:        char *c_namelist;       /* Pointer to other possible addresses */
        !            61:        char *c_output_name;    /* Hostname of connection for output */
        !            62:        char *c_data;           /* Data read from this fd */
        !            63:        struct timeval c_tv;    /* Time at which connection gets aborted */
        !            64:        TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */
        !            65: } con;
        !            66:
        !            67: TAILQ_HEAD(conlist, Connection) tq;    /* Timeout Queue */
        !            68: con *fdcon;
        !            69:
        !            70: /*
        !            71:  *  This is just a wrapper around fgets() to make it usable.
        !            72:  */
        !            73:
        !            74: /* Stress-test.  Increase this later. */
        !            75: #define LINEBUF_SIZE 16
        !            76:
        !            77: typedef struct {
        !            78:        char *buf;
        !            79:        unsigned int size;
        !            80:        int lineno;
        !            81:        const char *filename;
        !            82:        FILE *stream;
        !            83:        void (*errfun) (const char *,...);
        !            84: } Linebuf;
        !            85:
        !            86: static inline Linebuf *
        !            87: Linebuf_alloc(const char *filename, void (*errfun) (const char *,...))
        !            88: {
        !            89:        Linebuf *lb;
        !            90:
        !            91:        if (!(lb = malloc(sizeof(*lb)))) {
        !            92:                if (errfun)
        !            93:                        (*errfun) ("linebuf (%s): malloc failed\n", lb->filename);
        !            94:                return (NULL);
        !            95:        }
        !            96:        if (filename) {
        !            97:                lb->filename = filename;
        !            98:                if (!(lb->stream = fopen(filename, "r"))) {
        !            99:                        free(lb);
        !           100:                        if (errfun)
        !           101:                                (*errfun) ("%s: %s\n", filename, strerror(errno));
        !           102:                        return (NULL);
        !           103:                }
        !           104:        } else {
        !           105:                lb->filename = "(stdin)";
        !           106:                lb->stream = stdin;
        !           107:        }
        !           108:
        !           109:        if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) {
        !           110:                if (errfun)
        !           111:                        (*errfun) ("linebuf (%s): malloc failed\n", lb->filename);
        !           112:                free(lb);
        !           113:                return (NULL);
        !           114:        }
        !           115:        lb->errfun = errfun;
        !           116:        lb->lineno = 0;
        !           117:        return (lb);
        !           118: }
        !           119:
        !           120: static inline void
        !           121: Linebuf_free(Linebuf * lb)
        !           122: {
        !           123:        fclose(lb->stream);
        !           124:        free(lb->buf);
        !           125:        free(lb);
        !           126: }
        !           127:
        !           128: static inline void
        !           129: Linebuf_restart(Linebuf * lb)
        !           130: {
        !           131:        clearerr(lb->stream);
        !           132:        rewind(lb->stream);
        !           133:        lb->lineno = 0;
        !           134: }
        !           135:
        !           136: static inline int
        !           137: Linebuf_lineno(Linebuf * lb)
        !           138: {
        !           139:        return (lb->lineno);
        !           140: }
        !           141:
        !           142: static inline char *
        !           143: getline(Linebuf * lb)
        !           144: {
        !           145:        int n = 0;
        !           146:
        !           147:        lb->lineno++;
        !           148:        for (;;) {
        !           149:                /* Read a line */
        !           150:                if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) {
        !           151:                        if (ferror(lb->stream) && lb->errfun)
        !           152:                                (*lb->errfun) ("%s: %s\n", lb->filename, strerror(errno));
        !           153:                        return (NULL);
        !           154:                }
        !           155:                n = strlen(lb->buf);
        !           156:
        !           157:                /* Return it or an error if it fits */
        !           158:                if (n > 0 && lb->buf[n - 1] == '\n') {
        !           159:                        lb->buf[n - 1] = '\0';
        !           160:                        return (lb->buf);
        !           161:                }
        !           162:                if (n != lb->size - 1) {
        !           163:                        if (lb->errfun)
        !           164:                                (*lb->errfun) ("%s: skipping incomplete last line\n", lb->filename);
        !           165:                        return (NULL);
        !           166:                }
        !           167:                /* Double the buffer if we need more space */
        !           168:                if (!(lb->buf = realloc(lb->buf, (lb->size *= 2)))) {
        !           169:                        if (lb->errfun)
        !           170:                                (*lb->errfun) ("linebuf (%s): realloc failed\n", lb->filename);
        !           171:                        return (NULL);
        !           172:                }
        !           173:        }
        !           174: }
        !           175:
        !           176: static int
        !           177: fdlim_get(int hard)
        !           178: {
        !           179:        struct rlimit rlfd;
        !           180:        if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
        !           181:                return (-1);
        !           182:        if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
        !           183:                return 10000;
        !           184:        else
        !           185:                return hard ? rlfd.rlim_max : rlfd.rlim_cur;
        !           186: }
        !           187:
        !           188: static int
        !           189: fdlim_set(int lim)
        !           190: {
        !           191:        struct rlimit rlfd;
        !           192:        if (lim <= 0)
        !           193:                return (-1);
        !           194:        if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
        !           195:                return (-1);
        !           196:        rlfd.rlim_cur = lim;
        !           197:        if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
        !           198:                return (-1);
        !           199:        return (0);
        !           200: }
        !           201:
        !           202: /*
        !           203:  * This is an strsep function that returns a null field for adjacent
        !           204:  * separators.  This is the same as the 4.4BSD strsep, but different from the
        !           205:  * one in the GNU libc.
        !           206:  */
        !           207: inline char *
        !           208: xstrsep(char **str, const char *delim)
        !           209: {
        !           210:        char *s, *e;
        !           211:
        !           212:        if (!**str)
        !           213:                return (NULL);
        !           214:
        !           215:        s = *str;
        !           216:        e = s + strcspn(s, delim);
        !           217:
        !           218:        if (*e != '\0')
        !           219:                *e++ = '\0';
        !           220:        *str = e;
        !           221:
        !           222:        return (s);
        !           223: }
        !           224:
        !           225: /*
        !           226:  * Get the next non-null token (like GNU strsep).  Strsep() will return a
        !           227:  * null token for two adjacent separators, so we may have to loop.
        !           228:  */
        !           229: char *
        !           230: strnnsep(char **stringp, char *delim)
        !           231: {
        !           232:        char *tok;
        !           233:
        !           234:        do {
        !           235:                tok = xstrsep(stringp, delim);
        !           236:        } while (tok && *tok == '\0');
        !           237:        return (tok);
        !           238: }
        !           239:
        !           240: void
        !           241: keyprint(char *host, char *output_name, char *kd, int len)
        !           242: {
        !           243:        static Key *rsa;
        !           244:        static Buffer msg;
        !           245:
        !           246:        if (rsa == NULL) {
        !           247:                buffer_init(&msg);
        !           248:                rsa = key_new(KEY_RSA1);
        !           249:        }
        !           250:        buffer_append(&msg, kd, len);
        !           251:        buffer_consume(&msg, 8 - (len & 7));    /* padding */
        !           252:        if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) {
        !           253:                error("%s: invalid packet type", host);
        !           254:                buffer_clear(&msg);
        !           255:                return;
        !           256:        }
        !           257:        buffer_consume(&msg, 8);                /* cookie */
        !           258:
        !           259:        /* server key */
        !           260:        (void) buffer_get_int(&msg);
        !           261:        buffer_get_bignum(&msg, rsa->rsa->e);
        !           262:        buffer_get_bignum(&msg, rsa->rsa->n);
        !           263:
        !           264:        /* host key */
        !           265:        (void) buffer_get_int(&msg);
        !           266:        buffer_get_bignum(&msg, rsa->rsa->e);
        !           267:        buffer_get_bignum(&msg, rsa->rsa->n);
        !           268:        buffer_clear(&msg);
        !           269:
        !           270:        fprintf(stdout, "%s ", output_name ? output_name : host);
        !           271:        key_write(rsa, stdout);
        !           272:        fputs("\n", stdout);
        !           273: }
        !           274:
        !           275: int
        !           276: tcpconnect(char *host)
        !           277: {
        !           278:        struct addrinfo hints, *ai, *aitop;
        !           279:        char strport[NI_MAXSERV];
        !           280:        int gaierr, s = -1;
        !           281:
        !           282:        snprintf(strport, sizeof strport, "%d", PORT);
        !           283:        memset(&hints, 0, sizeof(hints));
        !           284:        hints.ai_family = family;
        !           285:        hints.ai_socktype = SOCK_STREAM;
        !           286:        if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
        !           287:                fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr));
        !           288:        for (ai = aitop; ai; ai = ai->ai_next) {
        !           289:                s = socket(ai->ai_family, SOCK_STREAM, 0);
        !           290:                if (s < 0) {
        !           291:                        error("socket: %s", strerror(errno));
        !           292:                        continue;
        !           293:                }
        !           294:                if (fcntl(s, F_SETFL, O_NDELAY) < 0)
        !           295:                        fatal("F_SETFL: %s", strerror(errno));
        !           296:                if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
        !           297:                    errno != EINPROGRESS)
        !           298:                        error("connect (`%s'): %s", host, strerror(errno));
        !           299:                else
        !           300:                        break;
        !           301:                close(s);
        !           302:                s = -1;
        !           303:        }
        !           304:        freeaddrinfo(aitop);
        !           305:        return s;
        !           306: }
        !           307:
        !           308: int
        !           309: conalloc(char *iname, char *oname)
        !           310: {
        !           311:        int s;
        !           312:        char *namebase, *name, *namelist;
        !           313:
        !           314:        namebase = namelist = xstrdup(iname);
        !           315:
        !           316:        do {
        !           317:                name = xstrsep(&namelist, ",");
        !           318:                if (!name) {
        !           319:                        free(namebase);
        !           320:                        return (-1);
        !           321:                }
        !           322:        } while ((s = tcpconnect(name)) < 0);
        !           323:
        !           324:        if (s >= maxfd)
        !           325:                fatal("conalloc: fdno %d too high\n", s);
        !           326:        if (fdcon[s].c_status)
        !           327:                fatal("conalloc: attempt to reuse fdno %d\n", s);
        !           328:
        !           329:        fdcon[s].c_fd = s;
        !           330:        fdcon[s].c_status = CS_CON;
        !           331:        fdcon[s].c_namebase = namebase;
        !           332:        fdcon[s].c_name = name;
        !           333:        fdcon[s].c_namelist = namelist;
        !           334:        fdcon[s].c_output_name = xstrdup(oname);
        !           335:        fdcon[s].c_data = (char *) &fdcon[s].c_plen;
        !           336:        fdcon[s].c_len = 4;
        !           337:        fdcon[s].c_off = 0;
        !           338:        gettimeofday(&fdcon[s].c_tv, NULL);
        !           339:        fdcon[s].c_tv.tv_sec += timeout;
        !           340:        TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
        !           341:        FD_SET(s, &read_wait);
        !           342:        ncon++;
        !           343:        return (s);
        !           344: }
        !           345:
        !           346: void
        !           347: confree(int s)
        !           348: {
        !           349:        close(s);
        !           350:        if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
        !           351:                fatal("confree: attempt to free bad fdno %d\n", s);
        !           352:        free(fdcon[s].c_namebase);
        !           353:        free(fdcon[s].c_output_name);
        !           354:        if (fdcon[s].c_status == CS_KEYS)
        !           355:                free(fdcon[s].c_data);
        !           356:        fdcon[s].c_status = CS_UNUSED;
        !           357:        TAILQ_REMOVE(&tq, &fdcon[s], c_link);
        !           358:        FD_CLR(s, &read_wait);
        !           359:        ncon--;
        !           360: }
        !           361:
        !           362: void
        !           363: contouch(int s)
        !           364: {
        !           365:        TAILQ_REMOVE(&tq, &fdcon[s], c_link);
        !           366:        gettimeofday(&fdcon[s].c_tv, NULL);
        !           367:        fdcon[s].c_tv.tv_sec += timeout;
        !           368:        TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
        !           369: }
        !           370:
        !           371: int
        !           372: conrecycle(int s)
        !           373: {
        !           374:        int ret;
        !           375:        con *c = &fdcon[s];
        !           376:        char *iname, *oname;
        !           377:
        !           378:        iname = xstrdup(c->c_namelist);
        !           379:        oname = c->c_output_name;
        !           380:        c->c_output_name = NULL;/* prevent it from being freed */
        !           381:        confree(s);
        !           382:        ret = conalloc(iname, oname);
        !           383:        free(iname);
        !           384:        return (ret);
        !           385: }
        !           386:
        !           387: void
        !           388: congreet(int s)
        !           389: {
        !           390:        char buf[80];
        !           391:        int n;
        !           392:        con *c = &fdcon[s];
        !           393:
        !           394:        n = read(s, buf, sizeof(buf));
        !           395:        if (n < 0) {
        !           396:                if (errno != ECONNREFUSED)
        !           397:                        error("read (%s): %s", c->c_name, strerror(errno));
        !           398:                conrecycle(s);
        !           399:                return;
        !           400:        }
        !           401:        if (buf[n - 1] != '\n') {
        !           402:                error("%s: bad greeting", c->c_name);
        !           403:                confree(s);
        !           404:                return;
        !           405:        }
        !           406:        buf[n - 1] = '\0';
        !           407:        fprintf(stderr, "# %s %s\n", c->c_name, buf);
        !           408:        n = snprintf(buf, sizeof buf, "SSH-1.5-OpenSSH-keyscan\r\n");
        !           409:        if (write(s, buf, n) != n) {
        !           410:                error("write (%s): %s", c->c_name, strerror(errno));
        !           411:                confree(s);
        !           412:                return;
        !           413:        }
        !           414:        c->c_status = CS_SIZE;
        !           415:        contouch(s);
        !           416: }
        !           417:
        !           418: void
        !           419: conread(int s)
        !           420: {
        !           421:        int n;
        !           422:        con *c = &fdcon[s];
        !           423:
        !           424:        if (c->c_status == CS_CON) {
        !           425:                congreet(s);
        !           426:                return;
        !           427:        }
        !           428:        n = read(s, c->c_data + c->c_off, c->c_len - c->c_off);
        !           429:        if (n < 0) {
        !           430:                error("read (%s): %s", c->c_name, strerror(errno));
        !           431:                confree(s);
        !           432:                return;
        !           433:        }
        !           434:        c->c_off += n;
        !           435:
        !           436:        if (c->c_off == c->c_len)
        !           437:                switch (c->c_status) {
        !           438:                case CS_SIZE:
        !           439:                        c->c_plen = htonl(c->c_plen);
        !           440:                        c->c_len = c->c_plen + 8 - (c->c_plen & 7);
        !           441:                        c->c_off = 0;
        !           442:                        c->c_data = xmalloc(c->c_len);
        !           443:                        c->c_status = CS_KEYS;
        !           444:                        break;
        !           445:                case CS_KEYS:
        !           446:                        keyprint(c->c_name, c->c_output_name, c->c_data, c->c_plen);
        !           447:                        confree(s);
        !           448:                        return;
        !           449:                        break;
        !           450:                default:
        !           451:                        fatal("conread: invalid status %d\n", c->c_status);
        !           452:                        break;
        !           453:                }
        !           454:
        !           455:        contouch(s);
        !           456: }
        !           457:
        !           458: void
        !           459: conloop(void)
        !           460: {
        !           461:        fd_set r, e;
        !           462:        struct timeval seltime, now;
        !           463:        int i;
        !           464:        con *c;
        !           465:
        !           466:        gettimeofday(&now, NULL);
        !           467:        c = tq.tqh_first;
        !           468:
        !           469:        if (c &&
        !           470:            (c->c_tv.tv_sec > now.tv_sec ||
        !           471:             (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) {
        !           472:                seltime = c->c_tv;
        !           473:                seltime.tv_sec -= now.tv_sec;
        !           474:                seltime.tv_usec -= now.tv_usec;
        !           475:                if ((int) seltime.tv_usec < 0) {
        !           476:                        seltime.tv_usec += 1000000;
        !           477:                        seltime.tv_sec--;
        !           478:                }
        !           479:        } else
        !           480:                seltime.tv_sec = seltime.tv_usec = 0;
        !           481:
        !           482:        r = e = read_wait;
        !           483:        select(maxfd, &r, NULL, &e, &seltime);
        !           484:        for (i = 0; i < maxfd; i++)
        !           485:                if (FD_ISSET(i, &e)) {
        !           486:                        error("%s: exception!", fdcon[i].c_name);
        !           487:                        confree(i);
        !           488:                } else if (FD_ISSET(i, &r))
        !           489:                        conread(i);
        !           490:
        !           491:        c = tq.tqh_first;
        !           492:        while (c &&
        !           493:               (c->c_tv.tv_sec < now.tv_sec ||
        !           494:                (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) {
        !           495:                int s = c->c_fd;
        !           496:                c = c->c_link.tqe_next;
        !           497:                conrecycle(s);
        !           498:        }
        !           499: }
        !           500:
        !           501: char *
        !           502: nexthost(int argc, char **argv)
        !           503: {
        !           504:        static Linebuf *lb;
        !           505:
        !           506:        for (;;) {
        !           507:                if (!lb) {
        !           508:                        if (argno >= argc)
        !           509:                                return (NULL);
        !           510:                        if (argv[argno][0] != '-')
        !           511:                                return (argv[argno++]);
        !           512:                        if (!strcmp(argv[argno], "--")) {
        !           513:                                if (++argno >= argc)
        !           514:                                        return (NULL);
        !           515:                                return (argv[argno++]);
        !           516:                        } else if (!strncmp(argv[argno], "-f", 2)) {
        !           517:                                char *fname;
        !           518:                                if (argv[argno][2])
        !           519:                                        fname = &argv[argno++][2];
        !           520:                                else if (++argno >= argc) {
        !           521:                                        error("missing filename for `-f'");
        !           522:                                        return (NULL);
        !           523:                                } else
        !           524:                                        fname = argv[argno++];
        !           525:                                if (!strcmp(fname, "-"))
        !           526:                                        fname = NULL;
        !           527:                                lb = Linebuf_alloc(fname, warn);
        !           528:                        } else
        !           529:                                error("ignoring invalid/misplaced option `%s'", argv[argno++]);
        !           530:                } else {
        !           531:                        char *line;
        !           532:                        line = getline(lb);
        !           533:                        if (line)
        !           534:                                return (line);
        !           535:                        Linebuf_free(lb);
        !           536:                        lb = NULL;
        !           537:                }
        !           538:        }
        !           539: }
        !           540:
        !           541: static void
        !           542: usage(void)
        !           543: {
        !           544:        fatal("usage: %s [-t timeout] { [--] host | -f file } ...\n", prog);
        !           545:        return;
        !           546: }
        !           547:
        !           548: int
        !           549: main(int argc, char **argv)
        !           550: {
        !           551:        char *host = NULL;
        !           552:
        !           553:        TAILQ_INIT(&tq);
        !           554:
        !           555:        if ((prog = strrchr(argv[0], '/')))
        !           556:                prog++;
        !           557:        else
        !           558:                prog = argv[0];
        !           559:
        !           560:        if (argc <= argno)
        !           561:                usage();
        !           562:
        !           563:        if (argv[1][0] == '-' && argv[1][1] == 't') {
        !           564:                argno++;
        !           565:                if (argv[1][2])
        !           566:                        timeout = atoi(&argv[1][2]);
        !           567:                else {
        !           568:                        if (argno >= argc)
        !           569:                                usage();
        !           570:                        timeout = atoi(argv[argno++]);
        !           571:                }
        !           572:                if (timeout <= 0)
        !           573:                        usage();
        !           574:        }
        !           575:        if (argc <= argno)
        !           576:                usage();
        !           577:
        !           578:        maxfd = fdlim_get(1);
        !           579:        if (maxfd < 0)
        !           580:                fatal("%s: fdlim_get: bad value\n", prog);
        !           581:        if (maxfd > MAXMAXFD)
        !           582:                maxfd = MAXMAXFD;
        !           583:        if (maxcon <= 0)
        !           584:                fatal("%s: not enough file descriptors\n", prog);
        !           585:        if (maxfd > fdlim_get(0))
        !           586:                fdlim_set(maxfd);
        !           587:        fdcon = xmalloc(maxfd * sizeof(con));
        !           588:
        !           589:        do {
        !           590:                while (ncon < maxcon) {
        !           591:                        char *name;
        !           592:
        !           593:                        host = nexthost(argc, argv);
        !           594:                        if (host == NULL)
        !           595:                                break;
        !           596:                        name = strnnsep(&host, " \t\n");
        !           597:                        conalloc(name, *host ? host : name);
        !           598:                }
        !           599:                conloop();
        !           600:        } while (host);
        !           601:        while (ncon > 0)
        !           602:                conloop();
        !           603:
        !           604:        return (0);
        !           605: }