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

Annotation of src/usr.bin/ssh/auth-rsa.c, Revision 1.12

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