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

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"
        !            19: RCSID("$Id: auth-rsa.c,v 1.4 1999/06/14 14:41:35 bg Exp $");
        !            20:
        !            21: #include "rsa.h"
        !            22: #include "packet.h"
        !            23: #include "xmalloc.h"
        !            24: #include "ssh.h"
        !            25: #include "ssh_md5.h"
        !            26: #include "mpaux.h"
        !            27: #include "uidswap.h"
        !            28:
        !            29: #include <ssl/rsa.h>
        !            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];
        !            63:   struct MD5Context md;
        !            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. */
        !            93:   len = (BN_num_bits(challenge) + 7) / 8;
        !            94:   assert(len <= 32 && len);
        !            95:   memset(buf, 0, 32);
        !            96:   BN_bn2bin(challenge, buf + 32 - len);
        !            97:   MD5Init(&md);
        !            98:   MD5Update(&md, buf, 32);
        !            99:   MD5Update(&md, session_id, 16);
        !           100:   MD5Final(mdbuf, &md);
        !           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
        !           130: auth_rsa(struct passwd *pw, BIGNUM *client_n)
        !           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:
        !           140:   /* Open the file containing the authorized keys. */
        !           141:   sprintf(line, "%.500s/%.100s", pw->pw_dir, SSH_USER_PERMITTED_KEYS);
        !           142:
        !           143:   /* Temporarily use the user's uid. */
        !           144:   temporarily_use_uid(pw->pw_uid);
        !           145:   if (stat(line, &st) < 0)
        !           146:     {
        !           147:       /* Restore the privileged uid. */
        !           148:       restore_uid();
        !           149:       return 0;
        !           150:     }
        !           151:   f = fopen(line, "r");
        !           152:   if (!f)
        !           153:     {
        !           154:       /* Restore the privileged uid. */
        !           155:       restore_uid();
        !           156:       packet_send_debug("Could not open %.900s for reading.", line);
        !           157:       packet_send_debug("If your home is on an NFS volume, it may need to be world-readable.");
        !           158:       return 0;
        !           159:     }
        !           160:
        !           161:   /* Flag indicating whether authentication has succeeded. */
        !           162:   authenticated = 0;
        !           163:
        !           164:   /* Initialize mp-int variables. */
        !           165:   e = BN_new();
        !           166:   n = BN_new();
        !           167:
        !           168:   /* Go though the accepted keys, looking for the current key.  If found,
        !           169:      perform a challenge-response dialog to verify that the user really has
        !           170:      the corresponding private key. */
        !           171:   while (fgets(line, sizeof(line), f))
        !           172:     {
        !           173:       char *cp;
        !           174:       char *options;
        !           175:
        !           176:       linenum++;
        !           177:
        !           178:       /* Skip leading whitespace. */
        !           179:       for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
        !           180:        ;
        !           181:
        !           182:       /* Skip empty and comment lines. */
        !           183:       if (!*cp || *cp == '\n' || *cp == '#')
        !           184:        continue;
        !           185:
        !           186:       /* Check if there are options for this key, and if so, save their
        !           187:         starting address and skip the option part for now.  If there are no
        !           188:         options, set the starting address to NULL. */
        !           189:       if (*cp < '0' || *cp > '9')
        !           190:        {
        !           191:          int quoted = 0;
        !           192:          options = cp;
        !           193:          for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++)
        !           194:            {
        !           195:              if (*cp == '\\' && cp[1] == '"')
        !           196:                cp++; /* Skip both */
        !           197:              else
        !           198:                if (*cp == '"')
        !           199:                  quoted = !quoted;
        !           200:            }
        !           201:        }
        !           202:       else
        !           203:        options = NULL;
        !           204:
        !           205:       /* Parse the key from the line. */
        !           206:       if (!auth_rsa_read_key(&cp, &bits, e, n))
        !           207:        {
        !           208:          debug("%.100s, line %lu: bad key syntax",
        !           209:                SSH_USER_PERMITTED_KEYS, linenum);
        !           210:          packet_send_debug("%.100s, line %lu: bad key syntax",
        !           211:                            SSH_USER_PERMITTED_KEYS, linenum);
        !           212:          continue;
        !           213:        }
        !           214:       /* cp now points to the comment part. */
        !           215:
        !           216:       /* Check if the we have found the desired key (identified by its
        !           217:         modulus). */
        !           218:       if (BN_cmp(n, client_n) != 0)
        !           219:        continue; /* Wrong key. */
        !           220:
        !           221:       /* We have found the desired key. */
        !           222:
        !           223:       /* Perform the challenge-response dialog for this key. */
        !           224:       if (!auth_rsa_challenge_dialog(bits, e, n))
        !           225:        {
        !           226:          /* Wrong response. */
        !           227:          log("Wrong response to RSA authentication challenge.");
        !           228:          packet_send_debug("Wrong response to RSA authentication challenge.");
        !           229:          continue;
        !           230:        }
        !           231:
        !           232:       /* Correct response.  The client has been successfully authenticated.
        !           233:         Note that we have not yet processed the options; this will be reset
        !           234:         if the options cause the authentication to be rejected. */
        !           235:       authenticated = 1;
        !           236:
        !           237:       /* RSA part of authentication was accepted.  Now process the options. */
        !           238:       if (options)
        !           239:        {
        !           240:          while (*options && *options != ' ' && *options != '\t')
        !           241:            {
        !           242:              cp = "no-port-forwarding";
        !           243:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           244:                {
        !           245:                  packet_send_debug("Port forwarding disabled.");
        !           246:                  no_port_forwarding_flag = 1;
        !           247:                  options += strlen(cp);
        !           248:                  goto next_option;
        !           249:                }
        !           250:              cp = "no-agent-forwarding";
        !           251:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           252:                {
        !           253:                  packet_send_debug("Agent forwarding disabled.");
        !           254:                  no_agent_forwarding_flag = 1;
        !           255:                  options += strlen(cp);
        !           256:                  goto next_option;
        !           257:                }
        !           258:              cp = "no-X11-forwarding";
        !           259:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           260:                {
        !           261:                  packet_send_debug("X11 forwarding disabled.");
        !           262:                  no_x11_forwarding_flag = 1;
        !           263:                  options += strlen(cp);
        !           264:                  goto next_option;
        !           265:                }
        !           266:              cp = "no-pty";
        !           267:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           268:                {
        !           269:                  packet_send_debug("Pty allocation disabled.");
        !           270:                  no_pty_flag = 1;
        !           271:                  options += strlen(cp);
        !           272:                  goto next_option;
        !           273:                }
        !           274:              cp = "command=\"";
        !           275:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           276:                {
        !           277:                  int i;
        !           278:                  options += strlen(cp);
        !           279:                  forced_command = xmalloc(strlen(options) + 1);
        !           280:                  i = 0;
        !           281:                  while (*options)
        !           282:                    {
        !           283:                      if (*options == '"')
        !           284:                        break;
        !           285:                      if (*options == '\\' && options[1] == '"')
        !           286:                        {
        !           287:                          options += 2;
        !           288:                          forced_command[i++] = '"';
        !           289:                          continue;
        !           290:                        }
        !           291:                      forced_command[i++] = *options++;
        !           292:                    }
        !           293:                  if (!*options)
        !           294:                    {
        !           295:                      debug("%.100s, line %lu: missing end quote",
        !           296:                            SSH_USER_PERMITTED_KEYS, linenum);
        !           297:                      packet_send_debug("%.100s, line %lu: missing end quote",
        !           298:                                        SSH_USER_PERMITTED_KEYS, linenum);
        !           299:                      continue;
        !           300:                    }
        !           301:                  forced_command[i] = 0;
        !           302:                  packet_send_debug("Forced command: %.900s", forced_command);
        !           303:                  options++;
        !           304:                  goto next_option;
        !           305:                }
        !           306:              cp = "environment=\"";
        !           307:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           308:                {
        !           309:                  int i;
        !           310:                  char *s;
        !           311:                  struct envstring *new_envstring;
        !           312:                  options += strlen(cp);
        !           313:                  s = xmalloc(strlen(options) + 1);
        !           314:                  i = 0;
        !           315:                  while (*options)
        !           316:                    {
        !           317:                      if (*options == '"')
        !           318:                        break;
        !           319:                      if (*options == '\\' && options[1] == '"')
        !           320:                        {
        !           321:                          options += 2;
        !           322:                          s[i++] = '"';
        !           323:                          continue;
        !           324:                        }
        !           325:                      s[i++] = *options++;
        !           326:                    }
        !           327:                  if (!*options)
        !           328:                    {
        !           329:                      debug("%.100s, line %lu: missing end quote",
        !           330:                            SSH_USER_PERMITTED_KEYS, linenum);
        !           331:                      packet_send_debug("%.100s, line %lu: missing end quote",
        !           332:                                        SSH_USER_PERMITTED_KEYS, linenum);
        !           333:                      continue;
        !           334:                    }
        !           335:                  s[i] = 0;
        !           336:                  packet_send_debug("Adding to environment: %.900s", s);
        !           337:                  debug("Adding to environment: %.900s", s);
        !           338:                  options++;
        !           339:                  new_envstring = xmalloc(sizeof(struct envstring));
        !           340:                  new_envstring->s = s;
        !           341:                  new_envstring->next = custom_environment;
        !           342:                  custom_environment = new_envstring;
        !           343:                  goto next_option;
        !           344:                }
        !           345:              cp = "from=\"";
        !           346:              if (strncmp(options, cp, strlen(cp)) == 0)
        !           347:                {
        !           348:                  char *patterns = xmalloc(strlen(options) + 1);
        !           349:                  int i;
        !           350:                  options += strlen(cp);
        !           351:                  i = 0;
        !           352:                  while (*options)
        !           353:                    {
        !           354:                      if (*options == '"')
        !           355:                        break;
        !           356:                      if (*options == '\\' && options[1] == '"')
        !           357:                        {
        !           358:                          options += 2;
        !           359:                          patterns[i++] = '"';
        !           360:                          continue;
        !           361:                        }
        !           362:                      patterns[i++] = *options++;
        !           363:                    }
        !           364:                  if (!*options)
        !           365:                    {
        !           366:                      debug("%.100s, line %lu: missing end quote",
        !           367:                            SSH_USER_PERMITTED_KEYS, linenum);
        !           368:                      packet_send_debug("%.100s, line %lu: missing end quote",
        !           369:                                        SSH_USER_PERMITTED_KEYS, linenum);
        !           370:                      continue;
        !           371:                    }
        !           372:                  patterns[i] = 0;
        !           373:                  options++;
        !           374:                  if (!match_hostname(get_canonical_hostname(), patterns,
        !           375:                                     strlen(patterns)) &&
        !           376:                      !match_hostname(get_remote_ipaddr(), patterns,
        !           377:                                      strlen(patterns)))
        !           378:                    {
        !           379:                      log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
        !           380:                          pw->pw_name, get_canonical_hostname(),
        !           381:                          get_remote_ipaddr());
        !           382:                      packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
        !           383:                                        get_canonical_hostname());
        !           384:                      xfree(patterns);
        !           385:                      authenticated = 0;
        !           386:                      break;
        !           387:                    }
        !           388:                  xfree(patterns);
        !           389:                  /* Host name matches. */
        !           390:                  goto next_option;
        !           391:                }
        !           392:            bad_option:
        !           393:              /* Unknown option. */
        !           394:              log("Bad options in %.100s file, line %lu: %.50s",
        !           395:                  SSH_USER_PERMITTED_KEYS, linenum, options);
        !           396:              packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
        !           397:                                SSH_USER_PERMITTED_KEYS, linenum, options);
        !           398:              authenticated = 0;
        !           399:              break;
        !           400:
        !           401:            next_option:
        !           402:              /* Skip the comma, and move to the next option (or break out
        !           403:                 if there are no more). */
        !           404:              if (!*options)
        !           405:                fatal("Bugs in auth-rsa.c option processing.");
        !           406:              if (*options == ' ' || *options == '\t')
        !           407:                break; /* End of options. */
        !           408:              if (*options != ',')
        !           409:                goto bad_option;
        !           410:              options++;
        !           411:              /* Process the next option. */
        !           412:              continue;
        !           413:            }
        !           414:        }
        !           415:
        !           416:       /* Break out of the loop if authentication was successful; otherwise
        !           417:         continue searching. */
        !           418:       if (authenticated)
        !           419:        break;
        !           420:     }
        !           421:
        !           422:   /* Restore the privileged uid. */
        !           423:   restore_uid();
        !           424:
        !           425:   /* Close the file. */
        !           426:   fclose(f);
        !           427:
        !           428:   /* Clear any mp-int variables. */
        !           429:   BN_clear_free(n);
        !           430:   BN_clear_free(e);
        !           431:
        !           432:   if (authenticated)
        !           433:     packet_send_debug("RSA authentication accepted.");
        !           434:
        !           435:   /* Return authentication result. */
        !           436:   return authenticated;
        !           437: }