LCOV - code coverage report
Current view: top level - util/cert/nss - cert.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 117 190 61.6 %
Date: 2016-06-29 Functions: 3 3 100.0 %

          Line data    Source code
       1             : 
       2             : /*
       3             :    SSSD - certificate handling utils - NSS version
       4             : 
       5             :    Copyright (C) Sumit Bose <sbose@redhat.com> 2015
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "config.h"
      22             : 
      23             : #include <nss.h>
      24             : #include <cert.h>
      25             : #include <base64.h>
      26             : #include <key.h>
      27             : #include <prerror.h>
      28             : #include <ocsp.h>
      29             : #include <talloc.h>
      30             : 
      31             : #include "util/crypto/sss_crypto.h"
      32             : #include "util/crypto/nss/nss_util.h"
      33             : #include "util/cert.h"
      34             : 
      35             : #define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----"
      36             : #define NS_CERT_TRAILER "-----END CERTIFICATE-----"
      37             : #define NS_CERT_HEADER_LEN  ((sizeof NS_CERT_HEADER) - 1)
      38             : #define NS_CERT_TRAILER_LEN ((sizeof NS_CERT_TRAILER) - 1)
      39             : 
      40           6 : errno_t sss_cert_der_to_pem(TALLOC_CTX *mem_ctx, const uint8_t *der_blob,
      41             :                             size_t der_size, char **pem, size_t *pem_size)
      42             : {
      43             : 
      44             :     CERTCertDBHandle *handle;
      45           6 :     CERTCertificate *cert = NULL;
      46             :     SECItem der_item;
      47           6 :     char *ascii_crlf = NULL;
      48             :     size_t ascii_crlf_len;
      49           6 :     char *ascii_lf = NULL;
      50           6 :     char *pem_cert_str = NULL;
      51             :     int ret;
      52             :     size_t c;
      53             :     size_t d;
      54             : 
      55             :     /* initialize NSS if needed */
      56           6 :     ret = nspr_nss_init();
      57           6 :     if (ret != EOK) {
      58           0 :         DEBUG(SSSDBG_OP_FAILURE, "nspr_nss_init failed.\n");
      59           0 :         return ret;
      60             :     }
      61             : 
      62           6 :     handle = CERT_GetDefaultCertDB();
      63             : 
      64           6 :     der_item.len = der_size;
      65           6 :     der_item.data = discard_const(der_blob);
      66             : 
      67           6 :     cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE);
      68           6 :     if (cert == NULL) {
      69           1 :         DEBUG(SSSDBG_OP_FAILURE, "CERT_NewTempCertificate failed.\n");
      70           1 :         return EINVAL;
      71             :     }
      72             : 
      73           5 :     ascii_crlf = BTOA_DataToAscii(cert->derCert.data, cert->derCert.len);
      74           5 :     if (ascii_crlf == NULL) {
      75           0 :         DEBUG(SSSDBG_OP_FAILURE, "BTOA_DataToAscii failed.\n");
      76           0 :         ret = EIO;
      77           0 :         goto done;
      78             :     }
      79             : 
      80           5 :     ascii_crlf_len = strlen(ascii_crlf) + 1;
      81           5 :     ascii_lf = talloc_size(mem_ctx, ascii_crlf_len * sizeof(char));
      82           5 :     if (ascii_lf == NULL) {
      83           0 :         DEBUG(SSSDBG_OP_FAILURE, "malloc failed.\n");
      84           0 :         ret = ENOMEM;
      85           0 :         goto done;
      86             :     }
      87             : 
      88           5 :     d = 0;
      89        7140 :     for (c = 0; c < ascii_crlf_len; c++) {
      90        7135 :         if (ascii_crlf[c] != '\r') {
      91        7030 :             ascii_lf[d++] = ascii_crlf[c];
      92             :         }
      93             :     }
      94             : 
      95           5 :     pem_cert_str = talloc_asprintf(mem_ctx, "%s\n%s\n%s\n", NS_CERT_HEADER,
      96             :                                                             ascii_lf,
      97             :                                                             NS_CERT_TRAILER);
      98           5 :     if (pem_cert_str == NULL) {
      99           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
     100           0 :         ret = ENOMEM;
     101           0 :         goto done;
     102             :     }
     103             : 
     104           5 :     if (pem_size != NULL) {
     105           5 :         *pem_size = strlen(pem_cert_str);
     106             :     }
     107             : 
     108           5 :     if (pem != NULL) {
     109           5 :         *pem = pem_cert_str;
     110           5 :         pem_cert_str = NULL;
     111             :     }
     112             : 
     113           5 :     ret = EOK;
     114             : done:
     115           5 :     talloc_free(pem_cert_str);
     116           5 :     talloc_free(ascii_lf);
     117           5 :     PORT_Free(ascii_crlf);
     118           5 :     CERT_DestroyCertificate(cert);
     119             : 
     120           5 :     return ret;
     121             : }
     122             : 
     123           4 : errno_t sss_cert_pem_to_der(TALLOC_CTX *mem_ctx, const char *pem,
     124             :                             uint8_t **_der_blob, size_t *_der_size)
     125             : {
     126             :     const char *ps;
     127             :     const char *pe;
     128             :     size_t pem_len;
     129           4 :     uint8_t *der_blob = NULL;
     130             :     unsigned int der_size; /* unsigned int to match 2nd parameter of
     131             :                               ATOB_AsciiToData */
     132             :     CERTCertDBHandle *handle;
     133           4 :     CERTCertificate *cert = NULL;
     134             :     SECItem der_item;
     135             :     int ret;
     136           4 :     char *b64 = NULL;
     137             : 
     138             :     /* initialize NSS if needed */
     139           4 :     ret = nspr_nss_init();
     140           4 :     if (ret != EOK) {
     141           0 :         DEBUG(SSSDBG_OP_FAILURE, "nspr_nss_init failed.\n");
     142           0 :         return ret;
     143             :     }
     144             : 
     145           4 :     if (pem == NULL || *pem == '\0') {
     146           2 :         return EINVAL;
     147             :     }
     148             : 
     149           2 :     pem_len = strlen(pem);
     150           2 :     if (pem_len <= NS_CERT_HEADER_LEN + NS_CERT_TRAILER_LEN) {
     151           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "PEM data too short.\n");
     152           0 :         return EINVAL;
     153             :     }
     154             : 
     155           2 :     if (strncmp(pem, NS_CERT_HEADER, NS_CERT_HEADER_LEN) != 0) {
     156           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Wrong PEM header.\n");
     157           0 :         return EINVAL;
     158             :     }
     159           2 :     if (pem[NS_CERT_HEADER_LEN] != '\n') {
     160           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing newline in PEM data.\n");
     161           0 :         return EINVAL;
     162             :     }
     163             : 
     164           2 :     pe = pem + pem_len - NS_CERT_TRAILER_LEN;
     165           2 :     if (pem[pem_len - 1] == '\n') {
     166           2 :         pe--;
     167             :     }
     168           2 :     if (strncmp(pe, NS_CERT_TRAILER, NS_CERT_TRAILER_LEN) != 0) {
     169           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Wrong PEM trailer.\n");
     170           0 :         return EINVAL;
     171             :     }
     172             : 
     173           2 :     ps = pem + NS_CERT_HEADER_LEN + 1;
     174             : 
     175           2 :     b64 = talloc_strndup(mem_ctx, ps, pe - ps);
     176           2 :     if(b64 == NULL) {
     177           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
     178           0 :         ret = ENOMEM;
     179           0 :         goto done;
     180             :     }
     181             : 
     182           2 :     der_blob = ATOB_AsciiToData(b64, &der_size);
     183           2 :     if (der_blob == NULL) {
     184           0 :         DEBUG(SSSDBG_OP_FAILURE, "ATOB_AsciiToData failed.\n");
     185           0 :         return EIO;
     186             :     }
     187             : 
     188           2 :     handle = CERT_GetDefaultCertDB();
     189             : 
     190           2 :     der_item.len = der_size;
     191           2 :     der_item.data = der_blob;
     192             : 
     193           2 :     cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE);
     194           2 :     if (cert == NULL) {
     195           0 :         DEBUG(SSSDBG_OP_FAILURE, "CERT_NewTempCertificate failed.\n");
     196           0 :         ret = EINVAL;
     197           0 :         goto done;
     198             :     }
     199             : 
     200           2 :     if (_der_blob != NULL) {
     201           2 :         *_der_blob = talloc_memdup(mem_ctx, cert->derCert.data,
     202             :                                    cert->derCert.len);
     203           2 :         if (*_der_blob == NULL) {
     204           0 :             DEBUG(SSSDBG_OP_FAILURE, "talloc_memdup failed.\n");
     205           0 :             ret = ENOMEM;
     206           0 :             goto done;
     207             :         }
     208             :     }
     209             : 
     210           2 :     if (_der_size != NULL) {
     211           2 :         *_der_size = cert->derCert.len;
     212             :     }
     213             : done:
     214           2 :     PORT_Free(der_blob);
     215           2 :     talloc_free(b64);
     216           2 :     CERT_DestroyCertificate(cert);
     217             : 
     218           2 :     return ret;
     219             : }
     220             : 
     221             : #define SSH_RSA_HEADER "ssh-rsa"
     222             : #define SSH_RSA_HEADER_LEN (sizeof(SSH_RSA_HEADER) - 1)
     223             : 
     224           1 : errno_t cert_to_ssh_key(TALLOC_CTX *mem_ctx, const char *ca_db,
     225             :                         const uint8_t *der_blob, size_t der_size,
     226             :                         struct cert_verify_opts *cert_verify_opts,
     227             :                         uint8_t **key, size_t *key_size)
     228             : {
     229             :     CERTCertDBHandle *handle;
     230           1 :     CERTCertificate *cert = NULL;
     231             :     SECItem der_item;
     232           1 :     SECKEYPublicKey *cert_pub_key = NULL;
     233             :     int ret;
     234             :     size_t size;
     235           1 :     uint8_t *buf = NULL;
     236             :     size_t c;
     237             :     NSSInitContext *nss_ctx;
     238           1 :     NSSInitParameters parameters = { 0 };
     239           1 :     parameters.length =  sizeof (parameters);
     240             :     SECStatus rv;
     241             :     SECStatus rv_verify;
     242             : 
     243           1 :     if (der_blob == NULL || der_size == 0) {
     244           0 :         return EINVAL;
     245             :     }
     246             : 
     247             :     /* initialize NSS with context, we might have already called
     248             :      * NSS_NoDB_Init() but for validation we need to have access to a DB with
     249             :      * the trusted issuer cert. Only NSS_InitContext will really open the DB
     250             :      * in this case. I'm not sure about how long validation might need e.g. if
     251             :      * CRLs or OSCP is enabled, maybe it would be better to run validation in
     252             :      * p11_child ? */
     253           1 :     nss_ctx = NSS_InitContext(ca_db, "", "", SECMOD_DB, &parameters,
     254             :                               NSS_INIT_READONLY);
     255           1 :     if (nss_ctx == NULL) {
     256           0 :         DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d].\n",
     257             :                                  PR_GetError());
     258           0 :         return EIO;
     259             :     }
     260             : 
     261           1 :     handle = CERT_GetDefaultCertDB();
     262             : 
     263           1 :     if (cert_verify_opts->do_ocsp) {
     264           0 :         rv = CERT_EnableOCSPChecking(handle);
     265           0 :         if (rv != SECSuccess) {
     266           0 :             DEBUG(SSSDBG_OP_FAILURE, "CERT_EnableOCSPChecking failed: [%d].\n",
     267             :                                      PR_GetError());
     268           0 :             return EIO;
     269             :         }
     270             : 
     271           0 :         if (cert_verify_opts->ocsp_default_responder != NULL
     272           0 :             && cert_verify_opts->ocsp_default_responder_signing_cert != NULL) {
     273           0 :             rv = CERT_SetOCSPDefaultResponder(handle,
     274           0 :                          cert_verify_opts->ocsp_default_responder,
     275           0 :                          cert_verify_opts->ocsp_default_responder_signing_cert);
     276           0 :             if (rv != SECSuccess) {
     277           0 :                 DEBUG(SSSDBG_OP_FAILURE,
     278             :                       "CERT_SetOCSPDefaultResponder failed: [%d].\n",
     279             :                       PR_GetError());
     280           0 :                 return EIO;
     281             :             }
     282             : 
     283           0 :             rv = CERT_EnableOCSPDefaultResponder(handle);
     284           0 :             if (rv != SECSuccess) {
     285           0 :                 DEBUG(SSSDBG_OP_FAILURE,
     286             :                       "CERT_EnableOCSPDefaultResponder failed: [%d].\n",
     287             :                       PR_GetError());
     288           0 :                 return EIO;
     289             :             }
     290             :         }
     291             :     }
     292             : 
     293           1 :     der_item.len = der_size;
     294           1 :     der_item.data = discard_const(der_blob);
     295             : 
     296           1 :     cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE);
     297           1 :     if (cert == NULL) {
     298           0 :         DEBUG(SSSDBG_OP_FAILURE, "CERT_NewTempCertificate failed.\n");
     299           0 :         ret = EINVAL;
     300           0 :         goto done;
     301             :     }
     302             : 
     303           1 :     if (cert_verify_opts->do_verification) {
     304           1 :         rv_verify = CERT_VerifyCertificateNow(handle, cert, PR_TRUE,
     305             :                                               certificateUsageSSLClient,
     306             :                                               NULL, NULL);
     307             : 
     308             :         /* Disable OCSP default responder so that NSS can shutdown properly */
     309           1 :         if (cert_verify_opts->do_ocsp
     310           0 :                 && cert_verify_opts->ocsp_default_responder != NULL
     311           0 :                 && cert_verify_opts->ocsp_default_responder_signing_cert
     312             :                                                                       != NULL) {
     313           0 :             rv = CERT_DisableOCSPDefaultResponder(handle);
     314           0 :             if (rv != SECSuccess) {
     315           0 :                 DEBUG(SSSDBG_OP_FAILURE,
     316             :                       "CERT_DisableOCSPDefaultResponder failed: [%d].\n",
     317             :                       PR_GetError());
     318             :             }
     319             :         }
     320             : 
     321           1 :         if (rv_verify != SECSuccess) {
     322           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "CERT_VerifyCertificateNow failed [%d].\n",
     323             :                                        PR_GetError());
     324           0 :             ret = EACCES;
     325           0 :             goto done;
     326             :         }
     327             :     }
     328             : 
     329           1 :     cert_pub_key = CERT_ExtractPublicKey(cert);
     330           1 :     if (cert_pub_key == NULL) {
     331           0 :         DEBUG(SSSDBG_OP_FAILURE, "CERT_ExtractPublicKey failed.\n");
     332           0 :         ret = EIO;
     333           0 :         goto done;
     334             :     }
     335             : 
     336           1 :     if (cert_pub_key->keyType != rsaKey) {
     337           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     338             :               "Expected RSA public key, found unsupported [%d].\n",
     339             :               cert_pub_key->keyType);
     340           0 :         ret = EINVAL;
     341           0 :         goto done;
     342             :     }
     343             : 
     344           1 :     size = SSH_RSA_HEADER_LEN + 3 * sizeof(uint32_t)
     345           1 :                 + cert_pub_key->u.rsa.modulus.len
     346           1 :                 + cert_pub_key->u.rsa.publicExponent.len
     347             :                 + 1; /* see comment about missing 00 below */
     348             : 
     349           1 :     buf = talloc_size(mem_ctx, size);
     350           1 :     if (buf == NULL) {
     351           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
     352           0 :         ret = ENOMEM;
     353           0 :         goto done;
     354             :     }
     355             : 
     356           1 :     c = 0;
     357             : 
     358           1 :     SAFEALIGN_SET_UINT32(buf, htobe32(SSH_RSA_HEADER_LEN), &c);
     359           1 :     safealign_memcpy(&buf[c], SSH_RSA_HEADER, SSH_RSA_HEADER_LEN, &c);
     360           1 :     SAFEALIGN_SET_UINT32(&buf[c],
     361             :                          htobe32(cert_pub_key->u.rsa.publicExponent.len), &c);
     362           1 :     safealign_memcpy(&buf[c], cert_pub_key->u.rsa.publicExponent.data,
     363           1 :                      cert_pub_key->u.rsa.publicExponent.len, &c);
     364             : 
     365             :     /* Looks like nss drops the leading 00 which afaik is added to make sure
     366             :      * the bigint is handled as positive number */
     367             :     /* TODO: make a better check if 00 must be added or not, e.g. ... & 0x80)
     368             :      */
     369           1 :     SAFEALIGN_SET_UINT32(&buf[c],
     370             :                          htobe32(cert_pub_key->u.rsa.modulus.len + 1 ), &c);
     371           1 :     SAFEALIGN_SETMEM_VALUE(&buf[c], '\0', unsigned char, &c);
     372           1 :     safealign_memcpy(&buf[c], cert_pub_key->u.rsa.modulus.data,
     373           1 :                      cert_pub_key->u.rsa.modulus.len, &c);
     374             : 
     375           1 :     *key = buf;
     376           1 :     *key_size = size;
     377             : 
     378           1 :     ret = EOK;
     379             : 
     380             : done:
     381           1 :     if (ret != EOK)  {
     382           0 :         talloc_free(buf);
     383             :     }
     384           1 :     SECKEY_DestroyPublicKey(cert_pub_key);
     385           1 :     CERT_DestroyCertificate(cert);
     386             : 
     387           1 :     rv = NSS_ShutdownContext(nss_ctx);
     388           1 :     if (rv != SECSuccess) {
     389           0 :         DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d].\n",
     390             :                                  PR_GetError());
     391             :     }
     392             : 
     393           1 :     return ret;
     394             : }

Generated by: LCOV version 1.10