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

Diff for /src/usr.bin/ssh/authfile.c between version 1.98 and 1.99

version 1.98, 2013/11/21 00:45:43 version 1.99, 2013/12/06 13:34:54
Line 13 
Line 13 
  * called by a name other than "ssh" or "Secure Shell".   * called by a name other than "ssh" or "Secure Shell".
  *   *
  *   *
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.   * Copyright (c) 2000, 2013 Markus Friedl.  All rights reserved.
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
Line 53 
Line 53 
 #include <string.h>  #include <string.h>
 #include <unistd.h>  #include <unistd.h>
   
   #include <util.h>
   
 #include "xmalloc.h"  #include "xmalloc.h"
 #include "cipher.h"  #include "cipher.h"
 #include "buffer.h"  #include "buffer.h"
Line 63 
Line 65 
 #include "rsa.h"  #include "rsa.h"
 #include "misc.h"  #include "misc.h"
 #include "atomicio.h"  #include "atomicio.h"
   #include "uuencode.h"
   
   /* openssh private key file format */
   #define MARK_BEGIN              "-----BEGIN OPENSSH PRIVATE KEY-----\n"
   #define MARK_END                "-----END OPENSSH PRIVATE KEY-----\n"
   #define KDFNAME                 "bcrypt"
   #define AUTH_MAGIC              "openssh-key-v1"
   #define SALT_LEN                16
   #define DEFAULT_CIPHERNAME      "aes256-cbc"
   #define DEFAULT_ROUNDS          16
   
 #define MAX_KEY_FILE_SIZE       (1024 * 1024)  #define MAX_KEY_FILE_SIZE       (1024 * 1024)
   
 /* Version identification string for SSH v1 identity files. */  /* Version identification string for SSH v1 identity files. */
 static const char authfile_id_string[] =  static const char authfile_id_string[] =
     "SSH PRIVATE KEY FILE FORMAT 1.1\n";      "SSH PRIVATE KEY FILE FORMAT 1.1\n";
   
   static int
   key_private_to_blob2(Key *prv, Buffer *blob, const char *passphrase,
       const char *comment, const char *ciphername, int rounds)
   {
           u_char *key, *cp, salt[SALT_LEN];
           size_t keylen, ivlen, blocksize, authlen;
           u_int len, check;
           int i, n;
           const Cipher *c;
           Buffer encoded, b, kdf;
           CipherContext ctx;
           const char *kdfname = KDFNAME;
   
           if (rounds <= 0)
                   rounds = DEFAULT_ROUNDS;
           if (passphrase == NULL || !strlen(passphrase)) {
                   ciphername = "none";
                   kdfname = "none";
           } else if (ciphername == NULL)
                   ciphername = DEFAULT_CIPHERNAME;
           else if (cipher_number(ciphername) != SSH_CIPHER_SSH2)
                   fatal("invalid cipher");
   
           if ((c = cipher_by_name(ciphername)) == NULL)
                   fatal("unknown cipher name");
           buffer_init(&kdf);
           blocksize = cipher_blocksize(c);
           keylen = cipher_keylen(c);
           ivlen = cipher_ivlen(c);
           authlen = cipher_authlen(c);
           key = xcalloc(1, keylen + ivlen);
           if (strcmp(kdfname, "none") != 0) {
                   arc4random_buf(salt, SALT_LEN);
                   if (bcrypt_pbkdf(passphrase, strlen(passphrase),
                       salt, SALT_LEN, key, keylen + ivlen, rounds) < 0)
                           fatal("bcrypt_pbkdf failed");
                   buffer_put_string(&kdf, salt, SALT_LEN);
                   buffer_put_int(&kdf, rounds);
           }
           cipher_init(&ctx, c, key, keylen, key + keylen , ivlen, 1);
           memset(key, 0, keylen + ivlen);
           free(key);
   
           buffer_init(&encoded);
           buffer_append(&encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC));
           buffer_put_cstring(&encoded, ciphername);
           buffer_put_cstring(&encoded, kdfname);
           buffer_put_string(&encoded, buffer_ptr(&kdf), buffer_len(&kdf));
           buffer_put_int(&encoded, 1);                    /* number of keys */
           key_to_blob(prv, &cp, &len);                    /* public key */
           buffer_put_string(&encoded, cp, len);
   
           memset(cp, 0, len);
           free(cp);
   
           buffer_free(&kdf);
   
           /* set up the buffer that will be encrypted */
           buffer_init(&b);
   
           /* Random check bytes */
           check = arc4random();
           buffer_put_int(&b, check);
           buffer_put_int(&b, check);
   
           /* append private key and comment*/
           key_private_serialize(prv, &b);
           buffer_put_cstring(&b, comment);
   
           /* padding */
           i = 0;
           while (buffer_len(&b) % blocksize)
                   buffer_put_char(&b, ++i & 0xff);
   
           /* length */
           buffer_put_int(&encoded, buffer_len(&b));
   
           /* encrypt */
           cp = buffer_append_space(&encoded, buffer_len(&b) + authlen);
           if (cipher_crypt(&ctx, 0, cp, buffer_ptr(&b), buffer_len(&b), 0,
               authlen) != 0)
                   fatal("%s: cipher_crypt failed", __func__);
           buffer_free(&b);
           cipher_cleanup(&ctx);
   
           /* uuencode */
           len = 2 * buffer_len(&encoded);
           cp = xmalloc(len);
           n = uuencode(buffer_ptr(&encoded), buffer_len(&encoded),
               (char *)cp, len);
           if (n < 0)
                   fatal("%s: uuencode", __func__);
   
           buffer_clear(blob);
           buffer_append(blob, MARK_BEGIN, sizeof(MARK_BEGIN) - 1);
           for (i = 0; i < n; i++) {
                   buffer_put_char(blob, cp[i]);
                   if (i % 70 == 69)
                           buffer_put_char(blob, '\n');
           }
           if (i % 70 != 69)
                   buffer_put_char(blob, '\n');
           buffer_append(blob, MARK_END, sizeof(MARK_END) - 1);
           free(cp);
   
           return buffer_len(blob);
   }
   
   static Key *
   key_parse_private2(Buffer *blob, int type, const char *passphrase,
       char **commentp)
   {
           u_char *key = NULL, *cp, *salt = NULL, pad, last;
           char *comment = NULL, *ciphername = NULL, *kdfname = NULL, *kdfp;
           u_int keylen = 0, ivlen, blocksize, slen, klen, len, rounds, nkeys;
           u_int check1, check2, m1len, m2len;
           size_t authlen;
           const Cipher *c;
           Buffer b, encoded, copy, kdf;
           CipherContext ctx;
           Key *k = NULL;
           int dlen, ret, i;
   
           buffer_init(&b);
           buffer_init(&kdf);
           buffer_init(&encoded);
           buffer_init(&copy);
   
           /* uudecode */
           m1len = sizeof(MARK_BEGIN) - 1;
           m2len = sizeof(MARK_END) - 1;
           cp = buffer_ptr(blob);
           len = buffer_len(blob);
           if (len < m1len || memcmp(cp, MARK_BEGIN, m1len)) {
                   debug("%s: missing begin marker", __func__);
                   goto out;
           }
           cp += m1len;
           len -= m1len;
           while (len) {
                   if (*cp != '\n' && *cp != '\r')
                           buffer_put_char(&encoded, *cp);
                   last = *cp;
                   len--;
                   cp++;
                   if (last == '\n') {
                           if (len >= m2len && !memcmp(cp, MARK_END, m2len)) {
                                   buffer_put_char(&encoded, '\0');
                                   break;
                           }
                   }
           }
           if (!len) {
                   debug("%s: no end marker", __func__);
                   goto out;
           }
           len = buffer_len(&encoded);
           if ((cp = buffer_append_space(&copy, len)) == NULL) {
                   error("%s: buffer_append_space", __func__);
                   goto out;
           }
           if ((dlen = uudecode(buffer_ptr(&encoded), cp, len)) < 0) {
                   error("%s: uudecode failed", __func__);
                   goto out;
           }
           if ((u_int)dlen > len) {
                   error("%s: crazy uudecode length %d > %u", __func__, dlen, len);
                   goto out;
           }
           buffer_consume_end(&copy, len - dlen);
           if (buffer_len(&copy) < sizeof(AUTH_MAGIC) ||
               memcmp(buffer_ptr(&copy), AUTH_MAGIC, sizeof(AUTH_MAGIC))) {
                   error("%s: bad magic", __func__);
                   goto out;
           }
           buffer_consume(&copy, sizeof(AUTH_MAGIC));
   
           ciphername = buffer_get_cstring_ret(&copy, NULL);
           if (ciphername == NULL ||
               (c = cipher_by_name(ciphername)) == NULL) {
                   error("%s: unknown cipher name", __func__);
                   goto out;
           }
           if ((passphrase == NULL || !strlen(passphrase)) &&
               strcmp(ciphername, "none") != 0) {
                   /* passphrase required */
                   goto out;
           }
           kdfname = buffer_get_cstring_ret(&copy, NULL);
           if (kdfname == NULL ||
               (!strcmp(kdfname, "none") && !strcmp(kdfname, "bcrypt"))) {
                   error("%s: unknown kdf name", __func__);
                   goto out;
           }
           if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) {
                   error("%s: cipher %s requires kdf", __func__, ciphername);
                   goto out;
           }
           /* kdf options */
           kdfp = buffer_get_string_ptr_ret(&copy, &klen);
           if (kdfp == NULL) {
                   error("%s: kdf options not set", __func__);
                   goto out;
           }
           if (klen > 0) {
                   if ((cp = buffer_append_space(&kdf, klen)) == NULL) {
                           error("%s: kdf alloc failed", __func__);
                           goto out;
                   }
                   memcpy(cp, kdfp, klen);
           }
           /* number of keys */
           if (buffer_get_int_ret(&nkeys, &copy) < 0) {
                   error("%s: key counter missing", __func__);
                   goto out;
           }
           if (nkeys != 1) {
                   error("%s: only one key supported", __func__);
                   goto out;
           }
           /* pubkey */
           if ((cp = buffer_get_string_ret(&copy, &len)) == NULL) {
                   error("%s: pubkey not found", __func__);
                   goto out;
           }
           free(cp); /* XXX check pubkey against decrypted private key */
   
           /* size of encrypted key blob */
           len = buffer_get_int(&copy);
           blocksize = cipher_blocksize(c);
           authlen = cipher_authlen(c);
           if (len < blocksize) {
                   error("%s: encrypted data too small", __func__);
                   goto out;
           }
           if (len % blocksize) {
                   error("%s: length not multiple of blocksize", __func__);
                   goto out;
           }
   
           /* setup key */
           keylen = cipher_keylen(c);
           ivlen = cipher_ivlen(c);
           key = xcalloc(1, keylen + ivlen);
           if (!strcmp(kdfname, "bcrypt")) {
                   if ((salt = buffer_get_string_ret(&kdf, &slen)) == NULL) {
                           error("%s: salt not set", __func__);
                           goto out;
                   }
                   if (buffer_get_int_ret(&rounds, &kdf) < 0) {
                           error("%s: rounds not set", __func__);
                           goto out;
                   }
                   if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
                       key, keylen + ivlen, rounds) < 0) {
                           error("%s: bcrypt_pbkdf failed", __func__);
                           goto out;
                   }
           }
   
           cp = buffer_append_space(&b, len);
           cipher_init(&ctx, c, key, keylen, key + keylen, ivlen, 0);
           ret = cipher_crypt(&ctx, 0, cp, buffer_ptr(&copy), len, 0, authlen);
           cipher_cleanup(&ctx);
           buffer_consume(&copy, len);
   
           /* fail silently on decryption errors */
           if (ret != 0) {
                   debug("%s: decrypt failed", __func__);
                   goto out;
           }
   
           if (buffer_len(&copy) != 0) {
                   error("%s: key blob has trailing data (len = %u)", __func__,
                       buffer_len(&copy));
                   goto out;
           }
   
           /* check bytes */
           if (buffer_get_int_ret(&check1, &b) < 0 ||
               buffer_get_int_ret(&check2, &b) < 0) {
                   error("check bytes missing");
                   goto out;
           }
           if (check1 != check2) {
                   debug("%s: decrypt failed: 0x%08x != 0x%08x", __func__,
                       check1, check2);
                   goto out;
           }
   
           k = key_private_deserialize(&b);
   
           /* comment */
           comment = buffer_get_cstring_ret(&b, NULL);
   
           i = 0;
           while (buffer_len(&b)) {
                   if (buffer_get_char_ret(&pad, &b) == -1 ||
                       pad != (++i & 0xff)) {
                           error("%s: bad padding", __func__);
                           key_free(k);
                           k = NULL;
                           goto out;
                   }
           }
   
           if (k && commentp) {
                   *commentp = comment;
                   comment = NULL;
           }
   
           /* XXX decode pubkey and check against private */
    out:
           free(ciphername);
           free(kdfname);
           free(salt);
           free(comment);
           if (key)
                   memset(key, 0, keylen + ivlen);
           free(key);
           buffer_free(&encoded);
           buffer_free(&copy);
           buffer_free(&kdf);
           buffer_free(&b);
           return k;
   }
   
 /*  /*
  * Serialises the authentication (private) key to a blob, encrypting it with   * Serialises the authentication (private) key to a blob, encrypting it with
  * passphrase.  The identification of the blob (lowest 64 bits of n) will   * passphrase.  The identification of the blob (lowest 64 bits of n) will
Line 144 
Line 483 
   
         cipher_set_key_string(&ciphercontext, cipher, passphrase,          cipher_set_key_string(&ciphercontext, cipher, passphrase,
             CIPHER_ENCRYPT);              CIPHER_ENCRYPT);
         cipher_crypt(&ciphercontext, 0, cp,          if (cipher_crypt(&ciphercontext, 0, cp,
             buffer_ptr(&buffer), buffer_len(&buffer), 0, 0);              buffer_ptr(&buffer), buffer_len(&buffer), 0, 0) != 0)
                   fatal("%s: cipher_crypt failed", __func__);
         cipher_cleanup(&ciphercontext);          cipher_cleanup(&ciphercontext);
         memset(&ciphercontext, 0, sizeof(ciphercontext));          memset(&ciphercontext, 0, sizeof(ciphercontext));
   
Line 228 
Line 568 
 /* Serialise "key" to buffer "blob" */  /* Serialise "key" to buffer "blob" */
 static int  static int
 key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,  key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
     const char *comment)      const char *comment, int force_new_format, const char *new_format_cipher,
       int new_format_rounds)
 {  {
         switch (key->type) {          switch (key->type) {
         case KEY_RSA1:          case KEY_RSA1:
Line 236 
Line 577 
         case KEY_DSA:          case KEY_DSA:
         case KEY_ECDSA:          case KEY_ECDSA:
         case KEY_RSA:          case KEY_RSA:
                   if (force_new_format) {
                           return key_private_to_blob2(key, blob, passphrase,
                               comment, new_format_cipher, new_format_rounds);
                   }
                 return key_private_pem_to_blob(key, blob, passphrase, comment);                  return key_private_pem_to_blob(key, blob, passphrase, comment);
         default:          default:
                 error("%s: cannot save key type %d", __func__, key->type);                  error("%s: cannot save key type %d", __func__, key->type);
Line 245 
Line 590 
   
 int  int
 key_save_private(Key *key, const char *filename, const char *passphrase,  key_save_private(Key *key, const char *filename, const char *passphrase,
     const char *comment)      const char *comment, int force_new_format, const char *new_format_cipher,
       int new_format_rounds)
 {  {
         Buffer keyblob;          Buffer keyblob;
         int success = 0;          int success = 0;
   
         buffer_init(&keyblob);          buffer_init(&keyblob);
         if (!key_private_to_blob(key, &keyblob, passphrase, comment))          if (!key_private_to_blob(key, &keyblob, passphrase, comment,
               force_new_format, new_format_cipher, new_format_rounds))
                 goto out;                  goto out;
         if (!key_save_private_blob(&keyblob, filename))          if (!key_save_private_blob(&keyblob, filename))
                 goto out;                  goto out;
Line 462 
Line 809 
         /* Rest of the buffer is encrypted.  Decrypt it using the passphrase. */          /* Rest of the buffer is encrypted.  Decrypt it using the passphrase. */
         cipher_set_key_string(&ciphercontext, cipher, passphrase,          cipher_set_key_string(&ciphercontext, cipher, passphrase,
             CIPHER_DECRYPT);              CIPHER_DECRYPT);
         cipher_crypt(&ciphercontext, 0, cp,          if (cipher_crypt(&ciphercontext, 0, cp,
             buffer_ptr(&copy), buffer_len(&copy), 0, 0);              buffer_ptr(&copy), buffer_len(&copy), 0, 0) != 0)
                   fatal("%s: cipher_crypt failed", __func__);
         cipher_cleanup(&ciphercontext);          cipher_cleanup(&ciphercontext);
         memset(&ciphercontext, 0, sizeof(ciphercontext));          memset(&ciphercontext, 0, sizeof(ciphercontext));
         buffer_free(&copy);          buffer_free(&copy);
Line 625 
Line 973 
 key_parse_private_type(Buffer *blob, int type, const char *passphrase,  key_parse_private_type(Buffer *blob, int type, const char *passphrase,
     char **commentp)      char **commentp)
 {  {
           Key *k;
   
         switch (type) {          switch (type) {
         case KEY_RSA1:          case KEY_RSA1:
                 return key_parse_private_rsa1(blob, passphrase, commentp);                  return key_parse_private_rsa1(blob, passphrase, commentp);
Line 632 
Line 982 
         case KEY_ECDSA:          case KEY_ECDSA:
         case KEY_RSA:          case KEY_RSA:
         case KEY_UNSPEC:          case KEY_UNSPEC:
                   if ((k = key_parse_private2(blob, type, passphrase, commentp)))
                           return k;
                 return key_parse_private_pem(blob, type, passphrase, commentp);                  return key_parse_private_pem(blob, type, passphrase, commentp);
         default:          default:
                 error("%s: cannot parse key type %d", __func__, type);                  error("%s: cannot parse key type %d", __func__, type);
Line 927 
Line 1279 
         fclose(f);          fclose(f);
         return ret;          return ret;
 }  }
   

Legend:
Removed from v.1.98  
changed lines
  Added in v.1.99