[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.6

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