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

Diff for /src/usr.bin/ssh/ssh-pkcs11.c between version 1.27 and 1.28

version 1.27, 2018/09/13 02:08:33 version 1.28, 2019/01/20 22:51:37
Line 1 
Line 1 
 /* $OpenBSD$ */  /* $OpenBSD$ */
 /*  /*
  * Copyright (c) 2010 Markus Friedl.  All rights reserved.   * Copyright (c) 2010 Markus Friedl.  All rights reserved.
    * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 20 
Line 21 
 #include <stdarg.h>  #include <stdarg.h>
 #include <stdio.h>  #include <stdio.h>
   
   #include <ctype.h>
 #include <string.h>  #include <string.h>
 #include <dlfcn.h>  #include <dlfcn.h>
   
   #include <openssl/ecdsa.h>
 #include <openssl/x509.h>  #include <openssl/x509.h>
   #include <openssl/err.h>
   
 #define CRYPTOKI_COMPAT  #define CRYPTOKI_COMPAT
 #include "pkcs11.h"  #include "pkcs11.h"
Line 60 
Line 64 
         CK_ULONG                slotidx;          CK_ULONG                slotidx;
         int                     (*orig_finish)(RSA *rsa);          int                     (*orig_finish)(RSA *rsa);
         RSA_METHOD              *rsa_method;          RSA_METHOD              *rsa_method;
           EC_KEY_METHOD           *ec_key_method;
         char                    *keyid;          char                    *keyid;
         int                     keyid_len;          int                     keyid_len;
 };  };
   
 int pkcs11_interactive = 0;  int pkcs11_interactive = 0;
   
   #ifdef HAVE_DLOPEN
   static void
   ossl_error(const char *msg)
   {
           unsigned long    e;
   
           while ((e = ERR_get_error()) != 0)
                   error("%s: %s: %.100s", __func__, msg,
                       ERR_error_string(e, NULL));
   }
   #endif
   
 int  int
 pkcs11_init(int interactive)  pkcs11_init(int interactive)
 {  {
Line 75 
Line 92 
 }  }
   
 /*  /*
  * finalize a provider shared libarary, it's no longer usable.   * finalize a provider shared library, it's no longer usable.
  * however, there might still be keys referencing this provider,   * however, there might still be keys referencing this provider,
  * so the actuall freeing of memory is handled by pkcs11_provider_unref().   * so the actual freeing of memory is handled by pkcs11_provider_unref().
  * this is called when a provider gets unregistered.   * this is called when a provider gets unregistered.
  */   */
 static void  static void
Line 116 
Line 133 
         if (--p->refcount <= 0) {          if (--p->refcount <= 0) {
                 if (p->valid)                  if (p->valid)
                         error("pkcs11_provider_unref: %p still valid", p);                          error("pkcs11_provider_unref: %p still valid", p);
                   free(p->name);
                 free(p->slotlist);                  free(p->slotlist);
                 free(p->slotinfo);                  free(p->slotinfo);
                 free(p);                  free(p);
Line 164 
Line 182 
         return (-1);          return (-1);
 }  }
   
   #ifdef HAVE_DLOPEN
 /* openssl callback for freeing an RSA key */  /* openssl callback for freeing an RSA key */
 static int  static int
 pkcs11_rsa_finish(RSA *rsa)  pkcs11_rsa_finish(RSA *rsa)
Line 211 
Line 230 
         return (ret);          return (ret);
 }  }
   
 /* openssl callback doing the actual signing operation */  
 static int  static int
 pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,  pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
     int padding)  
 {  {
         struct pkcs11_key       *k11;  
         struct pkcs11_slotinfo  *si;          struct pkcs11_slotinfo  *si;
         CK_FUNCTION_LIST        *f;          CK_FUNCTION_LIST        *f;
         CK_OBJECT_HANDLE        obj;          CK_OBJECT_HANDLE         obj;
         CK_ULONG                tlen = 0;          CK_RV                    rv;
         CK_RV                   rv;          CK_OBJECT_CLASS          private_key_class;
         CK_OBJECT_CLASS         private_key_class = CKO_PRIVATE_KEY;          CK_BBOOL                 true_val;
         CK_BBOOL                true_val = CK_TRUE;          CK_MECHANISM             mech;
         CK_MECHANISM            mech = {          CK_ATTRIBUTE             key_filter[3];
                 CKM_RSA_PKCS, NULL_PTR, 0  
         };  
         CK_ATTRIBUTE            key_filter[] = {  
                 {CKA_CLASS, &private_key_class, sizeof(private_key_class) },  
                 {CKA_ID, NULL, 0},  
                 {CKA_SIGN, &true_val, sizeof(true_val) }  
         };  
         char                    *pin = NULL, prompt[1024];          char                    *pin = NULL, prompt[1024];
         int                     rval = -1;  
   
         if ((k11 = RSA_get_app_data(rsa)) == NULL) {  
                 error("RSA_get_app_data failed for rsa %p", rsa);  
                 return (-1);  
         }  
         if (!k11->provider || !k11->provider->valid) {          if (!k11->provider || !k11->provider->valid) {
                 error("no pkcs11 (valid) provider for rsa %p", rsa);                  error("no pkcs11 (valid) provider found");
                 return (-1);                  return (-1);
         }          }
   
         f = k11->provider->function_list;          f = k11->provider->function_list;
         si = &k11->provider->slotinfo[k11->slotidx];          si = &k11->provider->slotinfo[k11->slotidx];
   
         if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {          if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
                 if (!pkcs11_interactive) {                  if (!pkcs11_interactive) {
                         error("need pin entry%s", (si->token.flags &                          error("need pin entry%s", (si->token.flags &
Line 273 
Line 279 
                 }                  }
                 si->logged_in = 1;                  si->logged_in = 1;
         }          }
   
           memset(&key_filter, 0, sizeof(key_filter));
           private_key_class = CKO_PRIVATE_KEY;
           key_filter[0].type = CKA_CLASS;
           key_filter[0].pValue = &private_key_class;
           key_filter[0].ulValueLen = sizeof(private_key_class);
   
           key_filter[1].type = CKA_ID;
         key_filter[1].pValue = k11->keyid;          key_filter[1].pValue = k11->keyid;
         key_filter[1].ulValueLen = k11->keyid_len;          key_filter[1].ulValueLen = k11->keyid_len;
   
           true_val = CK_TRUE;
           key_filter[2].type = CKA_SIGN;
           key_filter[2].pValue = &true_val;
           key_filter[2].ulValueLen = sizeof(true_val);
   
         /* try to find object w/CKA_SIGN first, retry w/o */          /* try to find object w/CKA_SIGN first, retry w/o */
         if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&          if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
             pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {              pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
                 error("cannot find private key");                  error("cannot find private key");
         } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {                  return (-1);
           }
   
           memset(&mech, 0, sizeof(mech));
           mech.mechanism = mech_type;
           mech.pParameter = NULL_PTR;
           mech.ulParameterLen = 0;
   
           if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
                 error("C_SignInit failed: %lu", rv);                  error("C_SignInit failed: %lu", rv);
         } else {                  return (-1);
                 /* XXX handle CKR_BUFFER_TOO_SMALL */  
                 tlen = RSA_size(rsa);  
                 rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);  
                 if (rv == CKR_OK)  
                         rval = tlen;  
                 else  
                         error("C_Sign failed: %lu", rv);  
         }          }
   
           return (0);
   }
   
   /* openssl callback doing the actual signing operation */
   static int
   pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
       int padding)
   {
           struct pkcs11_key       *k11;
           struct pkcs11_slotinfo  *si;
           CK_FUNCTION_LIST        *f;
           CK_ULONG                tlen = 0;
           CK_RV                   rv;
           int                     rval = -1;
   
           if ((k11 = RSA_get_app_data(rsa)) == NULL) {
                   error("RSA_get_app_data failed for rsa %p", rsa);
                   return (-1);
           }
   
           if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
                   error("pkcs11_get_key failed");
                   return (-1);
           }
   
           f = k11->provider->function_list;
           si = &k11->provider->slotinfo[k11->slotidx];
           tlen = RSA_size(rsa);
   
           /* XXX handle CKR_BUFFER_TOO_SMALL */
           rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
           if (rv == CKR_OK)
                   rval = tlen;
           else
                   error("C_Sign failed: %lu", rv);
   
         return (rval);          return (rval);
 }  }
   
Line 334 
Line 392 
         return (0);          return (0);
 }  }
   
   /* openssl callback doing the actual signing operation */
   static ECDSA_SIG *
   ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
       const BIGNUM *rp, EC_KEY *ec)
   {
           struct pkcs11_key       *k11;
           struct pkcs11_slotinfo  *si;
           CK_FUNCTION_LIST        *f;
           CK_ULONG                siglen = 0, bnlen;
           CK_RV                   rv;
           ECDSA_SIG               *ret = NULL;
           u_char                  *sig;
           const u_char            *cp;
   
           if ((k11 = EC_KEY_get_ex_data(ec, 0)) == NULL) {
                   ossl_error("EC_KEY_get_key_method_data failed for ec");
                   return (NULL);
           }
   
           if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
                   error("pkcs11_get_key failed");
                   return (NULL);
           }
   
           f = k11->provider->function_list;
           si = &k11->provider->slotinfo[k11->slotidx];
   
           siglen = ECDSA_size(ec);
           sig = xmalloc(siglen);
   
           /* XXX handle CKR_BUFFER_TOO_SMALL */
           rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
           if (rv != CKR_OK) {
                   error("C_Sign failed: %lu", rv);
                   goto done;
           }
           cp = sig;
           ret = d2i_ECDSA_SIG(NULL, &cp, siglen);
           if (ret == NULL) {
                   /*
                    * d2i_ECDSA_SIG failed, so sig does not point to a DER-encoded
                    * sequence, but to the concatenation r|s.
                    */
                   if (siglen < 64 || siglen > 132 || siglen % 2) {
                           ossl_error("d2i_ECDSA_SIG failed");
                           goto done;
                   }
                   bnlen = siglen/2;
                   if ((ret = ECDSA_SIG_new()) == NULL) {
                           error("ECDSA_SIG_new failed");
                           goto done;
                   }
                   if (BN_bin2bn(sig, bnlen, ret->r) == NULL ||
                       BN_bin2bn(sig+bnlen, bnlen, ret->s) == NULL) {
                           ossl_error("d2i_ECDSA_SIG failed");
                           ECDSA_SIG_free(ret);
                           ret = NULL;
                           goto done;
                   }
           }
    done:
           free(sig);
   
           return (ret);
   }
   
   static EC_KEY_METHOD *ec_key_method;
   
   static int
   pkcs11_ecdsa_start_wrapper(void)
   {
           int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
               unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
   
           if (ec_key_method != NULL)
                   return (0);
           ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
           if (ec_key_method == NULL)
                   return (-1);
           EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
           EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
           return (0);
   }
   
   static int
   pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
       CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
   {
           struct pkcs11_key       *k11;
   
           if (pkcs11_ecdsa_start_wrapper() == -1)
                   return (-1);
   
           k11 = xcalloc(1, sizeof(*k11));
           k11->provider = provider;
           provider->refcount++;   /* provider referenced by ECDSA key */
           k11->slotidx = slotidx;
           /* identify key object on smartcard */
           k11->keyid_len = keyid_attrib->ulValueLen;
           k11->keyid = xmalloc(k11->keyid_len);
           memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
           k11->ec_key_method = ec_key_method;
   
           EC_KEY_set_method(ec, k11->ec_key_method);
           EC_KEY_set_ex_data(ec, 0, k11);
   
           return (0);
   }
   
 /* remove trailing spaces */  /* remove trailing spaces */
 static void  static void
 rmspace(u_char *buf, size_t len)  rmspace(u_char *buf, size_t len)
Line 354 
Line 521 
  * if pin == NULL we delay login until key use   * if pin == NULL we delay login until key use
  */   */
 static int  static int
 pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin)  pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
       CK_ULONG user)
 {  {
         CK_RV                   rv;          CK_RV                   rv;
         CK_FUNCTION_LIST        *f;          CK_FUNCTION_LIST        *f;
         CK_SESSION_HANDLE       session;          CK_SESSION_HANDLE       session;
         int                     login_required;          int                     login_required, ret;
   
         f = p->function_list;          f = p->function_list;
         login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;          login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED;
         if (pin && login_required && !strlen(pin)) {          if (pin && login_required && !strlen(pin)) {
                 error("pin required");                  error("pin required");
                 return (-1);                  return (-SSH_PKCS11_ERR_PIN_REQUIRED);
         }          }
         if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|          if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
             CKF_SERIAL_SESSION, NULL, NULL, &session))              CKF_SERIAL_SESSION, NULL, NULL, &session))
Line 374 
Line 542 
                 return (-1);                  return (-1);
         }          }
         if (login_required && pin) {          if (login_required && pin) {
                 rv = f->C_Login(session, CKU_USER,                  rv = f->C_Login(session, user,
                     (u_char *)pin, strlen(pin));                      (u_char *)pin, strlen(pin));
                 if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {                  if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
                         error("C_Login failed: %lu", rv);                          error("C_Login failed: %lu", rv);
                           ret = (rv == CKR_PIN_LOCKED) ?
                               -SSH_PKCS11_ERR_PIN_LOCKED :
                               -SSH_PKCS11_ERR_LOGIN_FAIL;
                         if ((rv = f->C_CloseSession(session)) != CKR_OK)                          if ((rv = f->C_CloseSession(session)) != CKR_OK)
                                 error("C_CloseSession failed: %lu", rv);                                  error("C_CloseSession failed: %lu", rv);
                         return (-1);                          return (ret);
                 }                  }
                 p->slotinfo[slotidx].logged_in = 1;                  p->slotinfo[slotidx].logged_in = 1;
         }          }
Line 388 
Line 559 
         return (0);          return (0);
 }  }
   
 /*  
  * lookup public keys for token in slot identified by slotidx,  
  * add 'wrapped' public keys to the 'keysp' array and increment nkeys.  
  * keysp points to an (possibly empty) array with *nkeys keys.  
  */  
 static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG,  
     CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *)  
         __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE))));  
   
 static int  static int
 pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,  
     struct sshkey ***keysp, int *nkeys)  
 {  
         CK_OBJECT_CLASS         pubkey_class = CKO_PUBLIC_KEY;  
         CK_OBJECT_CLASS         cert_class = CKO_CERTIFICATE;  
         CK_ATTRIBUTE            pubkey_filter[] = {  
                 { CKA_CLASS, &pubkey_class, sizeof(pubkey_class) }  
         };  
         CK_ATTRIBUTE            cert_filter[] = {  
                 { CKA_CLASS, &cert_class, sizeof(cert_class) }  
         };  
         CK_ATTRIBUTE            pubkey_attribs[] = {  
                 { CKA_ID, NULL, 0 },  
                 { CKA_MODULUS, NULL, 0 },  
                 { CKA_PUBLIC_EXPONENT, NULL, 0 }  
         };  
         CK_ATTRIBUTE            cert_attribs[] = {  
                 { CKA_ID, NULL, 0 },  
                 { CKA_SUBJECT, NULL, 0 },  
                 { CKA_VALUE, NULL, 0 }  
         };  
   
         if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs,  
             keysp, nkeys) < 0 ||  
             pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs,  
             keysp, nkeys) < 0)  
                 return (-1);  
         return (0);  
 }  
   
 static int  
 pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)  pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
 {  {
         int i;          int i;
Line 439 
Line 570 
         return (0);          return (0);
 }  }
   
   static struct sshkey *
   pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
       CK_OBJECT_HANDLE *obj)
   {
           CK_ATTRIBUTE             key_attr[3];
           CK_SESSION_HANDLE        session;
           CK_FUNCTION_LIST        *f = NULL;
           CK_RV                    rv;
           EC_KEY                  *ec = NULL;
           EC_GROUP                *group = NULL;
           struct sshkey           *key = NULL;
           const unsigned char     *attrp = NULL;
           int                      i;
           int                      nid;
   
           memset(&key_attr, 0, sizeof(key_attr));
           key_attr[0].type = CKA_ID;
           key_attr[1].type = CKA_EC_POINT;
           key_attr[2].type = CKA_EC_PARAMS;
   
           session = p->slotinfo[slotidx].session;
           f = p->function_list;
   
           /* figure out size of the attributes */
           rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
           if (rv != CKR_OK) {
                   error("C_GetAttributeValue failed: %lu", rv);
                   return (NULL);
           }
   
           /*
            * Allow CKA_ID (always first attribute) to be empty, but
            * ensure that none of the others are zero length.
            * XXX assumes CKA_ID is always first.
            */
           if (key_attr[1].ulValueLen == 0 ||
               key_attr[2].ulValueLen == 0) {
                   error("invalid attribute length");
                   return (NULL);
           }
   
           /* allocate buffers for attributes */
           for (i = 0; i < 3; i++)
                   if (key_attr[i].ulValueLen > 0)
                           key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
   
           /* retrieve ID, public point and curve parameters of EC key */
           rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
           if (rv != CKR_OK) {
                   error("C_GetAttributeValue failed: %lu", rv);
                   goto fail;
           }
   
           ec = EC_KEY_new();
           if (ec == NULL) {
                   error("EC_KEY_new failed");
                   goto fail;
           }
   
           attrp = key_attr[2].pValue;
           group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
           if (group == NULL) {
                   ossl_error("d2i_ECPKParameters failed");
                   goto fail;
           }
   
           if (EC_KEY_set_group(ec, group) == 0) {
                   ossl_error("EC_KEY_set_group failed");
                   goto fail;
           }
   
           if (key_attr[1].ulValueLen <= 2) {
                   error("CKA_EC_POINT too small");
                   goto fail;
           }
   
           attrp = (const unsigned char *)key_attr[1].pValue;
           if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen) == NULL) {
                   /* try to skip DER header (octet string type and length byte) */
                   attrp = (const unsigned char *)key_attr[1].pValue + 2;
                   if (o2i_ECPublicKey(&ec, &attrp, key_attr[1].ulValueLen - 2)
                       == NULL) {
                           ossl_error("o2i_ECPublicKey failed");
                           goto fail;
                   }
           }
   
           nid = sshkey_ecdsa_key_to_nid(ec);
           if (nid < 0) {
                   error("couldn't get curve nid");
                   goto fail;
           }
   
           if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
                   goto fail;
   
           key = sshkey_new(KEY_UNSPEC);
           if (key == NULL) {
                   error("sshkey_new failed");
                   goto fail;
           }
   
           key->ecdsa = ec;
           key->ecdsa_nid = nid;
           key->type = KEY_ECDSA;
           key->flags |= SSHKEY_FLAG_EXT;
           ec = NULL;      /* now owned by key */
   
   fail:
           for (i = 0; i < 3; i++)
                   free(key_attr[i].pValue);
           if (ec)
                   EC_KEY_free(ec);
           if (group)
                   EC_GROUP_free(group);
   
           return (key);
   }
   
   static struct sshkey *
   pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
       CK_OBJECT_HANDLE *obj)
   {
           CK_ATTRIBUTE             key_attr[3];
           CK_SESSION_HANDLE        session;
           CK_FUNCTION_LIST        *f = NULL;
           CK_RV                    rv;
           RSA                     *rsa = NULL;
           BIGNUM                  *rsa_n, *rsa_e;
           struct sshkey           *key = NULL;
           int                      i;
   
           memset(&key_attr, 0, sizeof(key_attr));
           key_attr[0].type = CKA_ID;
           key_attr[1].type = CKA_MODULUS;
           key_attr[2].type = CKA_PUBLIC_EXPONENT;
   
           session = p->slotinfo[slotidx].session;
           f = p->function_list;
   
           /* figure out size of the attributes */
           rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
           if (rv != CKR_OK) {
                   error("C_GetAttributeValue failed: %lu", rv);
                   return (NULL);
           }
   
           /*
            * Allow CKA_ID (always first attribute) to be empty, but
            * ensure that none of the others are zero length.
            * XXX assumes CKA_ID is always first.
            */
           if (key_attr[1].ulValueLen == 0 ||
               key_attr[2].ulValueLen == 0) {
                   error("invalid attribute length");
                   return (NULL);
           }
   
           /* allocate buffers for attributes */
           for (i = 0; i < 3; i++)
                   if (key_attr[i].ulValueLen > 0)
                           key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
   
           /* retrieve ID, modulus and public exponent of RSA key */
           rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
           if (rv != CKR_OK) {
                   error("C_GetAttributeValue failed: %lu", rv);
                   goto fail;
           }
   
           rsa = RSA_new();
           if (rsa == NULL) {
                   error("RSA_new failed");
                   goto fail;
           }
   
           rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
           rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
           if (rsa_n == NULL || rsa_e == NULL) {
                   error("BN_bin2bn failed");
                   goto fail;
           }
           if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
                   fatal("%s: set key", __func__);
           rsa_n = rsa_e = NULL; /* transferred */
   
           if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
                   goto fail;
   
           key = sshkey_new(KEY_UNSPEC);
           if (key == NULL) {
                   error("sshkey_new failed");
                   goto fail;
           }
   
           key->rsa = rsa;
           key->type = KEY_RSA;
           key->flags |= SSHKEY_FLAG_EXT;
           rsa = NULL;     /* now owned by key */
   
   fail:
           for (i = 0; i < 3; i++)
                   free(key_attr[i].pValue);
           RSA_free(rsa);
   
           return (key);
   }
   
   static struct sshkey *
   pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
       CK_OBJECT_HANDLE *obj)
   {
           CK_ATTRIBUTE             cert_attr[3];
           CK_SESSION_HANDLE        session;
           CK_FUNCTION_LIST        *f = NULL;
           CK_RV                    rv;
           X509                    *x509 = NULL;
           EVP_PKEY                *evp;
           RSA                     *rsa = NULL;
           EC_KEY                  *ec = NULL;
           struct sshkey           *key = NULL;
           int                      i;
           int                      nid;
           const u_char             *cp;
   
           memset(&cert_attr, 0, sizeof(cert_attr));
           cert_attr[0].type = CKA_ID;
           cert_attr[1].type = CKA_SUBJECT;
           cert_attr[2].type = CKA_VALUE;
   
           session = p->slotinfo[slotidx].session;
           f = p->function_list;
   
           /* figure out size of the attributes */
           rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
           if (rv != CKR_OK) {
                   error("C_GetAttributeValue failed: %lu", rv);
                   return (NULL);
           }
   
           /*
            * Allow CKA_ID (always first attribute) to be empty, but
            * ensure that none of the others are zero length.
            * XXX assumes CKA_ID is always first.
            */
           if (cert_attr[1].ulValueLen == 0 ||
               cert_attr[2].ulValueLen == 0) {
                   error("invalid attribute length");
                   return (NULL);
           }
   
           /* allocate buffers for attributes */
           for (i = 0; i < 3; i++)
                   if (cert_attr[i].ulValueLen > 0)
                           cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
   
           /* retrieve ID, subject and value of certificate */
           rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
           if (rv != CKR_OK) {
                   error("C_GetAttributeValue failed: %lu", rv);
                   goto fail;
           }
   
           x509 = X509_new();
           if (x509 == NULL) {
                   error("x509_new failed");
                   goto fail;
           }
   
           cp = cert_attr[2].pValue;
           if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) {
                   error("d2i_x509 failed");
                   goto fail;
           }
   
           evp = X509_get_pubkey(x509);
           if (evp == NULL) {
                   error("X509_get_pubkey failed");
                   goto fail;
           }
   
           if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
                   if (EVP_PKEY_get0_RSA(evp) == NULL) {
                           error("invalid x509; no rsa key");
                           goto fail;
                   }
                   if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
                           error("RSAPublicKey_dup failed");
                           goto fail;
                   }
   
                   if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
                           goto fail;
   
                   key = sshkey_new(KEY_UNSPEC);
                   if (key == NULL) {
                           error("sshkey_new failed");
                           goto fail;
                   }
   
                   key->rsa = rsa;
                   key->type = KEY_RSA;
                   key->flags |= SSHKEY_FLAG_EXT;
                   rsa = NULL;     /* now owned by key */
           } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
                   /* XXX XXX fix accessor */
                   if (evp->pkey.ec == NULL) {
                           error("invalid x509; no ec key");
                           goto fail;
                   }
                   if ((ec = EC_KEY_dup(evp->pkey.ec)) == NULL) {
                           error("EC_KEY_dup failed");
                           goto fail;
                   }
   
                   nid = sshkey_ecdsa_key_to_nid(ec);
                   if (nid < 0) {
                           error("couldn't get curve nid");
                           goto fail;
                   }
   
                   if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
                           goto fail;
   
                   key = sshkey_new(KEY_UNSPEC);
                   if (key == NULL) {
                           error("sshkey_new failed");
                           goto fail;
                   }
   
                   key->ecdsa = ec;
                   key->ecdsa_nid = nid;
                   key->type = KEY_ECDSA;
                   key->flags |= SSHKEY_FLAG_EXT;
                   ec = NULL;      /* now owned by key */
           } else
                   error("unknown certificate key type");
   
   fail:
           for (i = 0; i < 3; i++)
                   free(cert_attr[i].pValue);
           X509_free(x509);
           RSA_free(rsa);
           EC_KEY_free(ec);
   
           return (key);
   }
   
   #if 0
 static int  static int
 have_rsa_key(const RSA *rsa)  have_rsa_key(const RSA *rsa)
 {  {
Line 447 
Line 927 
         RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);          RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
         return rsa_n != NULL && rsa_e != NULL;          return rsa_n != NULL && rsa_e != NULL;
 }  }
   #endif
   
   /*
    * lookup certificates for token in slot identified by slotidx,
    * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
    * keysp points to an (possibly empty) array with *nkeys keys.
    */
 static int  static int
 pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,  pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
     CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3],  
     struct sshkey ***keysp, int *nkeys)      struct sshkey ***keysp, int *nkeys)
 {  {
         struct sshkey           *key;          struct sshkey           *key = NULL;
         RSA                     *rsa;          CK_OBJECT_CLASS          key_class;
         X509                    *x509;          CK_ATTRIBUTE             key_attr[1];
         EVP_PKEY                *evp;          CK_SESSION_HANDLE        session;
         int                     i;          CK_FUNCTION_LIST        *f = NULL;
         const u_char            *cp;          CK_RV                    rv;
         CK_RV                   rv;          CK_OBJECT_HANDLE         obj;
         CK_OBJECT_HANDLE        obj;          CK_ULONG                 n = 0;
         CK_ULONG                nfound;          int                      ret = -1;
         CK_SESSION_HANDLE       session;  
         CK_FUNCTION_LIST        *f;  
   
         f = p->function_list;          memset(&key_attr, 0, sizeof(key_attr));
           memset(&obj, 0, sizeof(obj));
   
           key_class = CKO_CERTIFICATE;
           key_attr[0].type = CKA_CLASS;
           key_attr[0].pValue = &key_class;
           key_attr[0].ulValueLen = sizeof(key_class);
   
         session = p->slotinfo[slotidx].session;          session = p->slotinfo[slotidx].session;
         /* setup a filter the looks for public keys */          f = p->function_list;
         if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) {  
           rv = f->C_FindObjectsInit(session, key_attr, 1);
           if (rv != CKR_OK) {
                 error("C_FindObjectsInit failed: %lu", rv);                  error("C_FindObjectsInit failed: %lu", rv);
                 return (-1);                  goto fail;
         }          }
   
         while (1) {          while (1) {
                 /* XXX 3 attributes in attribs[] */                  CK_CERTIFICATE_TYPE     ck_cert_type;
                 for (i = 0; i < 3; i++) {  
                         attribs[i].pValue = NULL;                  rv = f->C_FindObjects(session, &obj, 1, &n);
                         attribs[i].ulValueLen = 0;                  if (rv != CKR_OK) {
                           error("C_FindObjects failed: %lu", rv);
                           goto fail;
                 }                  }
                 if ((rv = f->C_FindObjects(session, &obj, 1, &nfound)) != CKR_OK                  if (n == 0)
                     || nfound == 0)  
                         break;                          break;
                 /* found a key, so figure out size of the attributes */  
                 if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))                  memset(&ck_cert_type, 0, sizeof(ck_cert_type));
                     != CKR_OK) {                  memset(&key_attr, 0, sizeof(key_attr));
                   key_attr[0].type = CKA_CERTIFICATE_TYPE;
                   key_attr[0].pValue = &ck_cert_type;
                   key_attr[0].ulValueLen = sizeof(ck_cert_type);
   
                   rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
                   if (rv != CKR_OK) {
                         error("C_GetAttributeValue failed: %lu", rv);                          error("C_GetAttributeValue failed: %lu", rv);
                         continue;                          goto fail;
                 }                  }
                 /*  
                  * Allow CKA_ID (always first attribute) to be empty, but                  switch (ck_cert_type) {
                  * ensure that none of the others are zero length.                  case CKC_X_509:
                  * XXX assumes CKA_ID is always first.                          key = pkcs11_fetch_x509_pubkey(p, slotidx, &obj);
                  */                          break;
                 if (attribs[1].ulValueLen == 0 ||                  default:
                     attribs[2].ulValueLen == 0) {                          /* XXX print key type? */
                           error("skipping unsupported certificate type");
                   }
   
                   if (key == NULL) {
                           error("failed to fetch key");
                         continue;                          continue;
                 }                  }
                 /* allocate buffers for attributes */  
                 for (i = 0; i < 3; i++) {                  if (pkcs11_key_included(keysp, nkeys, key)) {
                         if (attribs[i].ulValueLen > 0) {                          sshkey_free(key);
                                 attribs[i].pValue = xmalloc(                  } else {
                                     attribs[i].ulValueLen);                          /* expand key array and add key */
                         }                          *keysp = xrecallocarray(*keysp, *nkeys,
                               *nkeys + 1, sizeof(struct sshkey *));
                           (*keysp)[*nkeys] = key;
                           *nkeys = *nkeys + 1;
                           debug("have %d keys", *nkeys);
                 }                  }
           }
   
                 /*          ret = 0;
                  * retrieve ID, modulus and public exponent of RSA key,  fail:
                  * or ID, subject and value for certificates.          rv = f->C_FindObjectsFinal(session);
                  */          if (rv != CKR_OK) {
                 rsa = NULL;                  error("C_FindObjectsFinal failed: %lu", rv);
                 if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3))                  ret = -1;
                     != CKR_OK) {          }
   
           return (ret);
   }
   
   /*
    * lookup public keys for token in slot identified by slotidx,
    * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
    * keysp points to an (possibly empty) array with *nkeys keys.
    */
   static int
   pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
       struct sshkey ***keysp, int *nkeys)
   {
           struct sshkey           *key = NULL;
           CK_OBJECT_CLASS          key_class;
           CK_ATTRIBUTE             key_attr[1];
           CK_SESSION_HANDLE        session;
           CK_FUNCTION_LIST        *f = NULL;
           CK_RV                    rv;
           CK_OBJECT_HANDLE         obj;
           CK_ULONG                 n = 0;
           int                      ret = -1;
   
           memset(&key_attr, 0, sizeof(key_attr));
           memset(&obj, 0, sizeof(obj));
   
           key_class = CKO_PUBLIC_KEY;
           key_attr[0].type = CKA_CLASS;
           key_attr[0].pValue = &key_class;
           key_attr[0].ulValueLen = sizeof(key_class);
   
           session = p->slotinfo[slotidx].session;
           f = p->function_list;
   
           rv = f->C_FindObjectsInit(session, key_attr, 1);
           if (rv != CKR_OK) {
                   error("C_FindObjectsInit failed: %lu", rv);
                   goto fail;
           }
   
           while (1) {
                   CK_KEY_TYPE     ck_key_type;
   
                   rv = f->C_FindObjects(session, &obj, 1, &n);
                   if (rv != CKR_OK) {
                           error("C_FindObjects failed: %lu", rv);
                           goto fail;
                   }
                   if (n == 0)
                           break;
   
                   memset(&ck_key_type, 0, sizeof(ck_key_type));
                   memset(&key_attr, 0, sizeof(key_attr));
                   key_attr[0].type = CKA_KEY_TYPE;
                   key_attr[0].pValue = &ck_key_type;
                   key_attr[0].ulValueLen = sizeof(ck_key_type);
   
                   rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
                   if (rv != CKR_OK) {
                         error("C_GetAttributeValue failed: %lu", rv);                          error("C_GetAttributeValue failed: %lu", rv);
                 } else if (attribs[1].type == CKA_MODULUS ) {                          goto fail;
                         if ((rsa = RSA_new()) == NULL) {                  }
                                 error("RSA_new failed");  
                         } else {  
                                 BIGNUM *rsa_n, *rsa_e;  
   
                                 rsa_n = BN_bin2bn(attribs[1].pValue,                  switch (ck_key_type) {
                                     attribs[1].ulValueLen, NULL);                  case CKK_RSA:
                                 rsa_e = BN_bin2bn(attribs[2].pValue,                          key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
                                     attribs[2].ulValueLen, NULL);                          break;
                                 if (rsa_n != NULL && rsa_e != NULL) {                  case CKK_ECDSA:
                                         if (!RSA_set0_key(rsa,                          key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
                                             rsa_n, rsa_e, NULL))                          break;
                                                 fatal("%s: set key", __func__);                  default:
                                         rsa_n = rsa_e = NULL; /* transferred */                          /* XXX print key type? */
                                 }                          error("skipping unsupported key type");
                                 BN_free(rsa_n);  
                                 BN_free(rsa_e);  
                         }  
                 } else {  
                         cp = attribs[2].pValue;  
                         if ((x509 = X509_new()) == NULL) {  
                                 error("X509_new failed");  
                         } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen)  
                             == NULL) {  
                                 error("d2i_X509 failed");  
                         } else if ((evp = X509_get_pubkey(x509)) == NULL ||  
                             EVP_PKEY_base_id(evp) != EVP_PKEY_RSA ||  
                             EVP_PKEY_get0_RSA(evp) == NULL) {  
                                 debug("X509_get_pubkey failed or no rsa");  
                         } else if ((rsa = RSAPublicKey_dup(  
                             EVP_PKEY_get0_RSA(evp))) == NULL) {  
                                 error("RSAPublicKey_dup");  
                         }  
                         X509_free(x509);  
                 }                  }
                 if (rsa && have_rsa_key(rsa) &&  
                     pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) {                  if (key == NULL) {
                         if ((key = sshkey_new(KEY_UNSPEC)) == NULL)                          error("failed to fetch key");
                                 fatal("sshkey_new failed");                          continue;
                         key->rsa = rsa;  
                         key->type = KEY_RSA;  
                         key->flags |= SSHKEY_FLAG_EXT;  
                         if (pkcs11_key_included(keysp, nkeys, key)) {  
                                 sshkey_free(key);  
                         } else {  
                                 /* expand key array and add key */  
                                 *keysp = xrecallocarray(*keysp, *nkeys,  
                                     *nkeys + 1, sizeof(struct sshkey *));  
                                 (*keysp)[*nkeys] = key;  
                                 *nkeys = *nkeys + 1;  
                                 debug("have %d keys", *nkeys);  
                         }  
                 } else if (rsa) {  
                         RSA_free(rsa);  
                 }                  }
                 for (i = 0; i < 3; i++)  
                         free(attribs[i].pValue);                  if (pkcs11_key_included(keysp, nkeys, key)) {
                           sshkey_free(key);
                   } else {
                           /* expand key array and add key */
                           *keysp = xrecallocarray(*keysp, *nkeys,
                               *nkeys + 1, sizeof(struct sshkey *));
                           (*keysp)[*nkeys] = key;
                           *nkeys = *nkeys + 1;
                           debug("have %d keys", *nkeys);
                   }
         }          }
         if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)  
           ret = 0;
   fail:
           rv = f->C_FindObjectsFinal(session);
           if (rv != CKR_OK) {
                 error("C_FindObjectsFinal failed: %lu", rv);                  error("C_FindObjectsFinal failed: %lu", rv);
         return (0);                  ret = -1;
           }
   
           return (ret);
 }  }
   
 #ifdef HAVE_DLOPEN  #ifdef WITH_PKCS11_KEYGEN
 /* register a new provider, fails if provider already exists */  #define FILL_ATTR(attr, idx, typ, val, len) \
 int          { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
 pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)  
   static struct sshkey *
   pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
       char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
 {  {
           struct pkcs11_slotinfo  *si;
           char                    *plabel = label ? label : "";
           int                      npub = 0, npriv = 0;
           CK_RV                    rv;
           CK_FUNCTION_LIST        *f;
           CK_SESSION_HANDLE        session;
           CK_BBOOL                 true_val = CK_TRUE, false_val = CK_FALSE;
           CK_OBJECT_HANDLE         pubKey, privKey;
           CK_ATTRIBUTE             tpub[16], tpriv[16];
           CK_MECHANISM             mech = {
               CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
           };
           CK_BYTE                  pubExponent[] = {
               0x01, 0x00, 0x01 /* RSA_F4 in bytes */
           };
   
           *err = 0;
   
           FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
           FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
           FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
           FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
               sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
           FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
               sizeof(pubExponent));
           FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
   
           FILL_ATTR(tpriv, npriv, CKA_TOKEN,  &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_LABEL,  plabel, strlen(plabel));
           FILL_ATTR(tpriv, npriv, CKA_PRIVATE,  &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_SENSITIVE,  &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_DECRYPT,  &false_val, sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_SIGN,  &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER,  &false_val,
               sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_UNWRAP,  &false_val, sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_DERIVE,  &false_val, sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
   
           f = p->function_list;
           si = &p->slotinfo[slotidx];
           session = si->session;
   
           if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
               &pubKey, &privKey)) != CKR_OK) {
                   error("%s: key generation failed: error 0x%lx", __func__, rv);
                   *err = rv;
                   return NULL;
           }
   
           return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
   }
   
   static int
   pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
   {
           size_t  i, len;
           char    ptr[3];
   
           if (dest)
                   *dest = NULL;
           if (rlen)
                   *rlen = 0;
   
           if ((len = strlen(hex)) % 2)
                   return -1;
           len /= 2;
   
           *dest = xmalloc(len);
   
           ptr[2] = '\0';
           for (i = 0; i < len; i++) {
                   ptr[0] = hex[2 * i];
                   ptr[1] = hex[(2 * i) + 1];
                   if (!isxdigit(ptr[0]) || !isxdigit(ptr[1]))
                           return -1;
                   (*dest)[i] = (unsigned char)strtoul(ptr, NULL, 16);
           }
   
           if (rlen)
                   *rlen = len;
   
           return 0;
   }
   
   static struct ec_curve_info {
           const char      *name;
           const char      *oid;
           const char      *oid_encoded;
           size_t           size;
   } ec_curve_infos[] = {
           {"prime256v1",  "1.2.840.10045.3.1.7",  "06082A8648CE3D030107", 256},
           {"secp384r1",   "1.3.132.0.34",         "06052B81040022",       384},
           {"secp521r1",   "1.3.132.0.35",         "06052B81040023",       521},
           {NULL,          NULL,                   NULL,                   0},
   };
   
   static struct sshkey *
   pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
       char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
   {
           struct pkcs11_slotinfo  *si;
           char                    *plabel = label ? label : "";
           int                      i;
           size_t                   ecparams_size;
           unsigned char           *ecparams = NULL;
           int                      npub = 0, npriv = 0;
           CK_RV                    rv;
           CK_FUNCTION_LIST        *f;
           CK_SESSION_HANDLE        session;
           CK_BBOOL                 true_val = CK_TRUE, false_val = CK_FALSE;
           CK_OBJECT_HANDLE         pubKey, privKey;
           CK_MECHANISM             mech = {
               CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
           };
           CK_ATTRIBUTE             tpub[16], tpriv[16];
   
           *err = 0;
   
           for (i = 0; ec_curve_infos[i].name; i++) {
                   if (ec_curve_infos[i].size == bits)
                           break;
           }
           if (!ec_curve_infos[i].name) {
                   error("%s: invalid key size %lu", __func__, bits);
                   return NULL;
           }
           if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
               &ecparams_size) == -1) {
                   error("%s: invalid oid", __func__);
                   return NULL;
           }
   
           FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
           FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
           FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
           FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
               sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
           FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
           FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
   
           FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
           FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
           FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
               sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
           FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
   
           f = p->function_list;
           si = &p->slotinfo[slotidx];
           session = si->session;
   
           if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
               &pubKey, &privKey)) != CKR_OK) {
                   error("%s: key generation failed: error 0x%lx", __func__, rv);
                   *err = rv;
                   return NULL;
           }
   
           return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey);
   }
   #endif /* WITH_PKCS11_KEYGEN */
   
   /*
    * register a new provider, fails if provider already exists. if
    * keyp is provided, fetch keys.
    */
   static int
   pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
       struct pkcs11_provider **providerp, CK_ULONG user)
   {
         int nkeys, need_finalize = 0;          int nkeys, need_finalize = 0;
           int ret = -1;
         struct pkcs11_provider *p = NULL;          struct pkcs11_provider *p = NULL;
         void *handle = NULL;          void *handle = NULL;
         CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);          CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
Line 590 
Line 1325 
         CK_TOKEN_INFO *token;          CK_TOKEN_INFO *token;
         CK_ULONG i;          CK_ULONG i;
   
         *keyp = NULL;          if (providerp == NULL)
                   goto fail;
           *providerp = NULL;
   
           if (keyp != NULL)
                   *keyp = NULL;
   
         if (pkcs11_provider_lookup(provider_id) != NULL) {          if (pkcs11_provider_lookup(provider_id) != NULL) {
                 debug("%s: provider already registered: %s",                  debug("%s: provider already registered: %s",
                     __func__, provider_id);                      __func__, provider_id);
                 goto fail;                  goto fail;
         }          }
         /* open shared pkcs11-libarary */          /* open shared pkcs11-library */
         if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {          if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
                 error("dlopen %s failed: %s", provider_id, dlerror());                  error("dlopen %s failed: %s", provider_id, dlerror());
                 goto fail;                  goto fail;
Line 642 
Line 1383 
                 goto fail;                  goto fail;
         }          }
         if (p->nslots == 0) {          if (p->nslots == 0) {
                 debug("%s: provider %s returned no slots", __func__,                  error("%s: provider %s returned no slots", __func__,
                     provider_id);                      provider_id);
                   ret = -SSH_PKCS11_ERR_NO_SLOTS;
                 goto fail;                  goto fail;
         }          }
         p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));          p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
Line 679 
Line 1421 
                     provider_id, (unsigned long)i,                      provider_id, (unsigned long)i,
                     token->label, token->manufacturerID, token->model,                      token->label, token->manufacturerID, token->model,
                     token->serialNumber, token->flags);                      token->serialNumber, token->flags);
                 /* open session, login with pin and retrieve public keys */                  /*
                 if (pkcs11_open_session(p, i, pin) == 0)                   * open session, login with pin and retrieve public
                    * keys (if keyp is provided)
                    */
                   if ((ret = pkcs11_open_session(p, i, pin, user)) == 0) {
                           if (keyp == NULL)
                                   continue;
                         pkcs11_fetch_keys(p, i, keyp, &nkeys);                          pkcs11_fetch_keys(p, i, keyp, &nkeys);
                           pkcs11_fetch_certs(p, i, keyp, &nkeys);
                   }
         }          }
         if (nkeys > 0) {  
                 TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);          /* now owned by caller */
                 p->refcount++;  /* add to provider list */          *providerp = p;
                 return (nkeys);  
         }          TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
         debug("%s: provider %s returned no keys", __func__, provider_id);          p->refcount++;  /* add to provider list */
         /* don't add the provider, since it does not have any keys */  
           return (nkeys);
 fail:  fail:
         if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)          if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
                 error("C_Finalize for provider %s failed: %lu",                  error("C_Finalize for provider %s failed: %lu",
                     provider_id, rv);                      provider_id, rv);
         if (p) {          if (p) {
                   free(p->name);
                 free(p->slotlist);                  free(p->slotlist);
                 free(p->slotinfo);                  free(p->slotinfo);
                 free(p);                  free(p);
         }          }
         if (handle)          if (handle)
                 dlclose(handle);                  dlclose(handle);
         return (-1);          return (ret);
 }  }
   
   /*
    * register a new provider and get number of keys hold by the token,
    * fails if provider already exists
    */
   int
   pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
   {
           struct pkcs11_provider *p = NULL;
           int nkeys;
   
           nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER);
   
           /* no keys found or some other error, de-register provider */
           if (nkeys <= 0 && p != NULL) {
                   TAILQ_REMOVE(&pkcs11_providers, p, next);
                   pkcs11_provider_finalize(p);
                   pkcs11_provider_unref(p);
           }
           if (nkeys == 0)
                   debug("%s: provider %s returned no keys", __func__,
                       provider_id);
   
           return (nkeys);
   }
   
   #ifdef WITH_PKCS11_KEYGEN
   struct sshkey *
   pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
       unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
   {
           struct pkcs11_provider  *p = NULL;
           struct pkcs11_slotinfo  *si;
           CK_FUNCTION_LIST        *f;
           CK_SESSION_HANDLE        session;
           struct sshkey           *k = NULL;
           int                      ret = -1, reset_pin = 0, reset_provider = 0;
           CK_RV                    rv;
   
           *err = 0;
   
           if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
                   debug("%s: provider \"%s\" available", __func__, provider_id);
           else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p,
               CKU_SO)) < 0) {
                   debug("%s: could not register provider %s", __func__,
                       provider_id);
                   goto out;
           } else
                   reset_provider = 1;
   
           f = p->function_list;
           si = &p->slotinfo[slotidx];
           session = si->session;
   
           if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
               CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
                   debug("%s: could not supply SO pin: %lu", __func__, rv);
                   reset_pin = 0;
           } else
                   reset_pin = 1;
   
           switch (type) {
           case KEY_RSA:
                   if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
                       bits, keyid, err)) == NULL) {
                           debug("%s: failed to generate RSA key", __func__);
                           goto out;
                   }
                   break;
           case KEY_ECDSA:
                   if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
                       bits, keyid, err)) == NULL) {
                           debug("%s: failed to generate ECDSA key", __func__);
                           goto out;
                   }
                   break;
           default:
                   *err = SSH_PKCS11_ERR_GENERIC;
                   debug("%s: unknown type %d", __func__, type);
                   goto out;
           }
   
   out:
           if (reset_pin)
                   f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
                       CK_INVALID_HANDLE);
   
           if (reset_provider)
                   pkcs11_del_provider(provider_id);
   
           return (k);
   }
   
   struct sshkey *
   pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
       unsigned char keyid, u_int32_t *err)
   {
           struct pkcs11_provider  *p = NULL;
           struct pkcs11_slotinfo  *si;
           struct sshkey           *k = NULL;
           int                      reset_pin = 0, reset_provider = 0;
           CK_ULONG                 nattrs;
           CK_FUNCTION_LIST        *f;
           CK_SESSION_HANDLE        session;
           CK_ATTRIBUTE             attrs[16];
           CK_OBJECT_CLASS          key_class;
           CK_KEY_TYPE              key_type;
           CK_OBJECT_HANDLE         obj = CK_INVALID_HANDLE;
           CK_RV                    rv;
   
           *err = 0;
   
           if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
                   debug("%s: using provider \"%s\"", __func__, provider_id);
           } else if (pkcs11_register_provider(provider_id, pin, NULL, &p,
               CKU_SO) < 0) {
                   debug("%s: could not register provider %s", __func__,
                       provider_id);
                   goto out;
           } else
                   reset_provider = 1;
   
           f = p->function_list;
           si = &p->slotinfo[slotidx];
           session = si->session;
   
           if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
               CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
                   debug("%s: could not supply SO pin: %lu", __func__, rv);
                   reset_pin = 0;
           } else
                   reset_pin = 1;
   
           /* private key */
           nattrs = 0;
           key_class = CKO_PRIVATE_KEY;
           FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
           FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
   
           if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
               obj != CK_INVALID_HANDLE) {
                   if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
                           debug("%s: could not destroy private key 0x%hhx",
                               __func__, keyid);
                           *err = rv;
                           goto out;
                   }
           }
   
           /* public key */
           nattrs = 0;
           key_class = CKO_PUBLIC_KEY;
           FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
           FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
   
           if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
               obj != CK_INVALID_HANDLE) {
   
                   /* get key type */
                   nattrs = 0;
                   FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
                       sizeof(key_type));
                   rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
                   if (rv != CKR_OK) {
                           debug("%s: could not get key type of public key 0x%hhx",
                               __func__, keyid);
                           *err = rv;
                           key_type = -1;
                   }
                   if (key_type == CKK_RSA)
                           k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
                   else if (key_type == CKK_ECDSA)
                           k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
   
                   if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
                           debug("%s: could not destroy public key 0x%hhx",
                               __func__, keyid);
                           *err = rv;
                           goto out;
                   }
           }
   
   out:
           if (reset_pin)
                   f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
                       CK_INVALID_HANDLE);
   
           if (reset_provider)
                   pkcs11_del_provider(provider_id);
   
           return (k);
   }
   #endif /* WITH_PKCS11_KEYGEN */
 #else  #else
 int  int
 pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)  pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp)
Line 710 
Line 1655 
         error("dlopen() not supported");          error("dlopen() not supported");
         return (-1);          return (-1);
 }  }
 #endif  #endif /* HAVE_DLOPEN */

Legend:
Removed from v.1.27  
changed lines
  Added in v.1.28