~ubuntu-branches/ubuntu/saucy/curl/saucy-201307251546

« back to all changes in this revision

Viewing changes to lib/nss.c

  • Committer: Bazaar Package Importer
  • Author(s): Ramakrishnan Muthukrishnan
  • Date: 2011-02-28 19:35:36 UTC
  • mto: (3.6.1 experimental) (1.3.1)
  • mto: This revision was merged to the branch mainline in revision 47.
  • Revision ID: james.westby@ubuntu.com-20110228193536-p3a9jawxxofcsz7o
Tags: upstream-7.21.4
ImportĀ upstreamĀ versionĀ 7.21.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
 *                            | (__| |_| |  _ <| |___
6
6
 *                             \___|\___/|_| \_\_____|
7
7
 *
8
 
 * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
 
8
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
9
9
 *
10
10
 * This software is licensed as described in the file COPYING, which
11
11
 * you should have received as part of this distribution. The terms
42
42
#include "strequal.h"
43
43
#include "select.h"
44
44
#include "sslgen.h"
 
45
#include "llist.h"
45
46
 
46
47
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
47
48
#include <curl/mprintf.h>
90
91
  PRInt32 version; /* protocol version valid for this cipher */
91
92
} cipher_s;
92
93
 
93
 
#define PK11_SETATTRS(x,id,v,l) (x)->type = (id);       \
94
 
  (x)->pValue=(v); (x)->ulValueLen = (l)
 
94
#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do {  \
 
95
  CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++);                 \
 
96
  ptr->type = (_type);                                      \
 
97
  ptr->pValue = (_val);                                     \
 
98
  ptr->ulValueLen = (_len);                                 \
 
99
} while(0)
95
100
 
96
101
#define CERT_NewTempCertificate __CERT_NewTempCertificate
97
102
 
277
282
  return 0;
278
283
}
279
284
 
280
 
static char *fmt_nickname(char *str, bool *nickname_alloc)
281
 
{
282
 
  char *nickname = NULL;
283
 
  *nickname_alloc = FALSE;
284
 
 
285
 
  if(is_file(str)) {
286
 
    char *n = strrchr(str, '/');
287
 
    if(n) {
288
 
      *nickname_alloc = TRUE;
289
 
      n++; /* skip last slash */
290
 
      nickname = aprintf("PEM Token #%d:%s", 1, n);
291
 
    }
292
 
    return nickname;
293
 
  }
294
 
 
295
 
  return str;
296
 
}
 
285
/* Return on heap allocated filename/nickname of a certificate.  The returned
 
286
 * string should be later deallocated using free().  *is_nickname is set to TRUE
 
287
 * if the given string is treated as nickname; FALSE if the given string is
 
288
 * treated as file name.
 
289
 */
 
290
static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind,
 
291
                          bool *is_nickname)
 
292
{
 
293
  const char *str = data->set.str[cert_kind];
 
294
  const char *n;
 
295
  *is_nickname = TRUE;
 
296
 
 
297
  if(!is_file(str))
 
298
    /* no such file exists, use the string as nickname */
 
299
    return strdup(str);
 
300
 
 
301
  /* search the last slash; we require at least one slash in a file name */
 
302
  n = strrchr(str, '/');
 
303
  if(!n) {
 
304
    infof(data, "warning: certificate file name \"%s\" handled as nickname; "
 
305
          "please use \"./%s\" to force file name\n", str, str);
 
306
    return strdup(str);
 
307
  }
 
308
 
 
309
  /* we'll use the PEM reader to read the certificate from file */
 
310
  *is_nickname = FALSE;
 
311
 
 
312
  n++; /* skip last slash */
 
313
  return aprintf("PEM Token #%d:%s", 1, n);
 
314
}
 
315
 
 
316
#ifdef HAVE_PK11_CREATEGENERICOBJECT
 
317
/* Call PK11_CreateGenericObject() with the given obj_class and filename.  If
 
318
 * the call succeeds, append the object handle to the list of objects so that
 
319
 * the object can be destroyed in Curl_nss_close(). */
 
320
static CURLcode nss_create_object(struct ssl_connect_data *ssl,
 
321
                                  CK_OBJECT_CLASS obj_class,
 
322
                                  const char *filename, bool cacert)
 
323
{
 
324
  PK11SlotInfo *slot;
 
325
  PK11GenericObject *obj;
 
326
  CK_BBOOL cktrue = CK_TRUE;
 
327
  CK_BBOOL ckfalse = CK_FALSE;
 
328
  CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
 
329
  int attr_cnt = 0;
 
330
 
 
331
  const int slot_id = (cacert) ? 0 : 1;
 
332
  char *slot_name = aprintf("PEM Token #%d", slot_id);
 
333
  if(!slot_name)
 
334
    return CURLE_OUT_OF_MEMORY;
 
335
 
 
336
  slot = PK11_FindSlotByName(slot_name);
 
337
  free(slot_name);
 
338
  if(!slot)
 
339
    return CURLE_SSL_CERTPROBLEM;
 
340
 
 
341
  PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
 
342
  PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
 
343
  PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename,
 
344
                strlen(filename) + 1);
 
345
 
 
346
  if(CKO_CERTIFICATE == obj_class) {
 
347
    CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse);
 
348
    PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval));
 
349
  }
 
350
 
 
351
  obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
 
352
  PK11_FreeSlot(slot);
 
353
  if(!obj)
 
354
    return CURLE_SSL_CERTPROBLEM;
 
355
 
 
356
  if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
 
357
    PK11_DestroyGenericObject(obj);
 
358
    return CURLE_OUT_OF_MEMORY;
 
359
  }
 
360
 
 
361
  return CURLE_OK;
 
362
}
 
363
 
 
364
/* Destroy the NSS object whose handle is given by ptr.  This function is
 
365
 * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy
 
366
 * NSS objects in Curl_nss_close() */
 
367
static void nss_destroy_object(void *user, void *ptr)
 
368
{
 
369
  PK11GenericObject *obj = (PK11GenericObject *)ptr;
 
370
  (void) user;
 
371
  PK11_DestroyGenericObject(obj);
 
372
}
 
373
#endif
297
374
 
298
375
static int nss_load_cert(struct ssl_connect_data *ssl,
299
376
                         const char *filename, PRBool cacert)
300
377
{
301
378
#ifdef HAVE_PK11_CREATEGENERICOBJECT
302
 
  CK_SLOT_ID slotID;
303
 
  PK11SlotInfo * slot = NULL;
304
 
  CK_ATTRIBUTE *attrs;
305
 
  CK_ATTRIBUTE theTemplate[20];
306
 
  CK_BBOOL cktrue = CK_TRUE;
307
 
  CK_BBOOL ckfalse = CK_FALSE;
308
 
  CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
309
 
  char slotname[SLOTSIZE];
 
379
  /* All CA and trust objects go into slot 0. Other slots are used
 
380
   * for storing certificates.
 
381
   */
 
382
  const int slot_id = (cacert) ? 0 : 1;
310
383
#endif
311
384
  CERTCertificate *cert;
312
385
  char *nickname = NULL;
333
406
  }
334
407
 
335
408
#ifdef HAVE_PK11_CREATEGENERICOBJECT
336
 
  attrs = theTemplate;
337
 
 
338
 
  /* All CA and trust objects go into slot 0. Other slots are used
339
 
   * for storing certificates. With each new user certificate we increment
340
 
   * the slot count. We only support 1 user certificate right now.
341
 
   */
342
 
  if(cacert)
343
 
    slotID = 0;
344
 
  else
345
 
    slotID = 1;
346
 
 
347
 
  snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID);
348
 
 
349
 
  nickname = aprintf("PEM Token #%ld:%s", slotID, n);
 
409
  nickname = aprintf("PEM Token #%d:%s", slot_id, n);
350
410
  if(!nickname)
351
411
    return 0;
352
412
 
353
 
  slot = PK11_FindSlotByName(slotname);
354
 
 
355
 
  if(!slot) {
356
 
    free(nickname);
357
 
    return 0;
358
 
  }
359
 
 
360
 
  PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) );
361
 
  attrs++;
362
 
  PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) );
363
 
  attrs++;
364
 
  PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename,
365
 
                strlen(filename)+1);
366
 
  attrs++;
367
 
  if(cacert) {
368
 
    PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) );
369
 
  }
370
 
  else {
371
 
    PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) );
372
 
  }
373
 
  attrs++;
374
 
 
375
 
  /* This load the certificate in our PEM module into the appropriate
376
 
   * slot.
377
 
   */
378
 
  ssl->cacert[slotID] = PK11_CreateGenericObject(slot, theTemplate, 4,
379
 
                                                 PR_FALSE /* isPerm */);
380
 
 
381
 
  PK11_FreeSlot(slot);
382
 
 
383
 
  if(ssl->cacert[slotID] == NULL) {
384
 
    free(nickname);
385
 
    return 0;
386
 
  }
 
413
  if(CURLE_OK != nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert)) {
 
414
    free(nickname);
 
415
    return 0;
 
416
  }
 
417
 
387
418
#else
388
419
  /* We don't have PK11_CreateGenericObject but a file-based cert was passed
389
420
   * in. We need to fail.
503
534
                        char *key_file)
504
535
{
505
536
#ifdef HAVE_PK11_CREATEGENERICOBJECT
506
 
  PK11SlotInfo * slot = NULL;
507
 
  CK_ATTRIBUTE *attrs;
508
 
  CK_ATTRIBUTE theTemplate[20];
509
 
  CK_BBOOL cktrue = CK_TRUE;
510
 
  CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY;
511
 
  CK_SLOT_ID slotID;
512
 
  char slotname[SLOTSIZE];
513
 
  struct ssl_connect_data *sslconn = &conn->ssl[sockindex];
514
 
 
515
 
  attrs = theTemplate;
516
 
 
517
 
  /* FIXME: grok the various file types */
518
 
 
519
 
  slotID = 1; /* hardcoded for now */
520
 
 
521
 
  snprintf(slotname, sizeof(slotname), "PEM Token #%ld", slotID);
522
 
  slot = PK11_FindSlotByName(slotname);
523
 
 
524
 
  if(!slot)
525
 
    return 0;
526
 
 
527
 
  PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++;
528
 
  PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++;
529
 
  PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)key_file,
530
 
                strlen(key_file)+1); attrs++;
531
 
 
532
 
  /* When adding an encrypted key the PKCS#11 will be set as removed */
533
 
  sslconn->key = PK11_CreateGenericObject(slot, theTemplate, 3,
534
 
                                          PR_FALSE /* isPerm */);
535
 
  if(sslconn->key == NULL) {
 
537
  PK11SlotInfo *slot;
 
538
  SECStatus status;
 
539
  struct ssl_connect_data *ssl = conn->ssl;
 
540
  (void)sockindex; /* unused */
 
541
 
 
542
  if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) {
536
543
    PR_SetError(SEC_ERROR_BAD_KEY, 0);
537
544
    return 0;
538
545
  }
539
546
 
 
547
  slot = PK11_FindSlotByName("PEM Token #1");
 
548
  if(!slot)
 
549
    return 0;
 
550
 
540
551
  /* This will force the token to be seen as re-inserted */
541
552
  SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
542
553
  PK11_IsPresent(slot);
543
554
 
544
 
  /* parg is initialized in nss_Init_Tokens() */
545
 
  if(PK11_Authenticate(slot, PR_TRUE,
546
 
                       conn->data->set.str[STRING_KEY_PASSWD]) != SECSuccess) {
547
 
 
548
 
    PK11_FreeSlot(slot);
549
 
    return 0;
550
 
  }
 
555
  status = PK11_Authenticate(slot, PR_TRUE,
 
556
                             conn->data->set.str[STRING_KEY_PASSWD]);
551
557
  PK11_FreeSlot(slot);
552
 
 
553
 
  return 1;
 
558
  return (SECSuccess == status) ? 1 : 0;
554
559
#else
555
560
  /* If we don't have PK11_CreateGenericObject then we can't load a file-based
556
561
   * key.
557
562
   */
558
563
  (void)conn; /* unused */
559
564
  (void)key_file; /* unused */
 
565
  (void)sockindex; /* unused */
560
566
  return 0;
561
567
#endif
562
568
}
1050
1056
      connssl->client_nickname = NULL;
1051
1057
    }
1052
1058
#ifdef HAVE_PK11_CREATEGENERICOBJECT
1053
 
    if(connssl->key)
1054
 
      (void)PK11_DestroyGenericObject(connssl->key);
1055
 
    if(connssl->cacert[1])
1056
 
      (void)PK11_DestroyGenericObject(connssl->cacert[1]);
1057
 
    if(connssl->cacert[0])
1058
 
      (void)PK11_DestroyGenericObject(connssl->cacert[0]);
 
1059
    /* destroy all NSS objects in order to avoid failure of NSS shutdown */
 
1060
    Curl_llist_destroy(connssl->obj_list, NULL);
1059
1061
#endif
1060
1062
    connssl->handle = NULL;
1061
1063
  }
1095
1097
static Curl_recv nss_recv;
1096
1098
static Curl_send nss_send;
1097
1099
 
 
1100
static CURLcode nss_load_ca_certificates(struct connectdata *conn,
 
1101
                                         int sockindex)
 
1102
{
 
1103
  struct SessionHandle *data = conn->data;
 
1104
  const char *cafile = data->set.ssl.CAfile;
 
1105
  const char *capath = data->set.ssl.CApath;
 
1106
 
 
1107
  if(cafile && !nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE))
 
1108
    return CURLE_SSL_CACERT_BADFILE;
 
1109
 
 
1110
  if(capath) {
 
1111
    struct_stat st;
 
1112
    if(stat(capath, &st) == -1)
 
1113
      return CURLE_SSL_CACERT_BADFILE;
 
1114
 
 
1115
    if(S_ISDIR(st.st_mode)) {
 
1116
      PRDirEntry *entry;
 
1117
      PRDir *dir = PR_OpenDir(capath);
 
1118
      if(!dir)
 
1119
        return CURLE_SSL_CACERT_BADFILE;
 
1120
 
 
1121
      while((entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN))) {
 
1122
        char *fullpath = aprintf("%s/%s", capath, entry->name);
 
1123
        if(!fullpath) {
 
1124
          PR_CloseDir(dir);
 
1125
          return CURLE_OUT_OF_MEMORY;
 
1126
        }
 
1127
 
 
1128
        if(!nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
 
1129
          /* This is purposefully tolerant of errors so non-PEM files can
 
1130
           * be in the same directory */
 
1131
          infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath);
 
1132
 
 
1133
        free(fullpath);
 
1134
      }
 
1135
 
 
1136
      PR_CloseDir(dir);
 
1137
    }
 
1138
    else
 
1139
      infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath);
 
1140
  }
 
1141
 
 
1142
  infof(data, "  CAfile: %s\n  CApath: %s\n",
 
1143
      cafile ? cafile : "none",
 
1144
      capath ? capath : "none");
 
1145
 
 
1146
  return CURLE_OK;
 
1147
}
 
1148
 
1098
1149
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
1099
1150
{
1100
1151
  PRInt32 err;
1117
1168
  connssl->data = data;
1118
1169
 
1119
1170
#ifdef HAVE_PK11_CREATEGENERICOBJECT
1120
 
  connssl->cacert[0] = NULL;
1121
 
  connssl->cacert[1] = NULL;
1122
 
  connssl->key = NULL;
 
1171
  /* list of all NSS objects we need to destroy in Curl_nss_close() */
 
1172
  connssl->obj_list = Curl_llist_alloc(nss_destroy_object);
 
1173
  if(!connssl->obj_list)
 
1174
    return CURLE_OUT_OF_MEMORY;
1123
1175
#endif
1124
1176
 
1125
1177
  /* FIXME. NSS doesn't support multiple databases open at the same time. */
1236
1288
                           NULL) != SECSuccess)
1237
1289
    goto error;
1238
1290
 
1239
 
  if(!data->set.ssl.verifypeer)
1240
 
    /* skip the verifying of the peer */
1241
 
    ;
1242
 
  else if(data->set.ssl.CAfile) {
1243
 
    int rc = nss_load_cert(&conn->ssl[sockindex], data->set.ssl.CAfile,
1244
 
                           PR_TRUE);
1245
 
    if(!rc) {
1246
 
      curlerr = CURLE_SSL_CACERT_BADFILE;
1247
 
      goto error;
1248
 
    }
1249
 
  }
1250
 
  else if(data->set.ssl.CApath) {
1251
 
    struct_stat st;
1252
 
    PRDir      *dir;
1253
 
    PRDirEntry *entry;
1254
 
 
1255
 
    if(stat(data->set.ssl.CApath, &st) == -1) {
1256
 
      curlerr = CURLE_SSL_CACERT_BADFILE;
1257
 
      goto error;
1258
 
    }
1259
 
 
1260
 
    if(S_ISDIR(st.st_mode)) {
1261
 
      int rc;
1262
 
 
1263
 
      dir = PR_OpenDir(data->set.ssl.CApath);
1264
 
      do {
1265
 
        entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN);
1266
 
 
1267
 
        if(entry) {
1268
 
          char fullpath[PATH_MAX];
1269
 
 
1270
 
          snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath,
1271
 
                   entry->name);
1272
 
          rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE);
1273
 
          /* FIXME: check this return value! */
1274
 
        }
1275
 
        /* This is purposefully tolerant of errors so non-PEM files
1276
 
         * can be in the same directory */
1277
 
      } while(entry != NULL);
1278
 
      PR_CloseDir(dir);
1279
 
    }
1280
 
  }
1281
 
  infof(data,
1282
 
        "  CAfile: %s\n"
1283
 
        "  CApath: %s\n",
1284
 
        data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
1285
 
        data->set.ssl.CApath ? data->set.ssl.CApath : "none");
 
1291
  if(data->set.ssl.verifypeer && (CURLE_OK !=
 
1292
        (curlerr = nss_load_ca_certificates(conn, sockindex))))
 
1293
    goto error;
1286
1294
 
1287
1295
  if (data->set.ssl.CRLfile) {
1288
1296
    if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) {
1295
1303
  }
1296
1304
 
1297
1305
  if(data->set.str[STRING_CERT]) {
1298
 
    bool nickname_alloc = FALSE;
1299
 
    char *nickname = fmt_nickname(data->set.str[STRING_CERT], &nickname_alloc);
 
1306
    bool is_nickname;
 
1307
    char *nickname = fmt_nickname(data, STRING_CERT, &is_nickname);
1300
1308
    if(!nickname)
1301
1309
      return CURLE_OUT_OF_MEMORY;
1302
1310
 
1303
 
    if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
1304
 
                   data->set.str[STRING_KEY])) {
 
1311
    if(!is_nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
 
1312
                                   data->set.str[STRING_KEY])) {
1305
1313
      /* failf() is already done in cert_stuff() */
1306
 
      if(nickname_alloc)
1307
 
        free(nickname);
 
1314
      free(nickname);
1308
1315
      return CURLE_SSL_CERTPROBLEM;
1309
1316
    }
1310
1317
 
1311
 
    /* this "takes over" the pointer to the allocated name or makes a
1312
 
       dup of it */
1313
 
    connssl->client_nickname = nickname_alloc?nickname:strdup(nickname);
1314
 
    if(!connssl->client_nickname)
1315
 
      return CURLE_OUT_OF_MEMORY;
1316
 
 
 
1318
    /* store the nickname for SelectClientCert() called during handshake */
 
1319
    connssl->client_nickname = nickname;
1317
1320
  }
1318
1321
  else
1319
1322
    connssl->client_nickname = NULL;
1344
1347
  SSL_SetURL(connssl->handle, conn->host.name);
1345
1348
 
1346
1349
  /* check timeout situation */
1347
 
  time_left = Curl_timeleft(conn, NULL, TRUE);
 
1350
  time_left = Curl_timeleft(data, NULL, TRUE);
1348
1351
  if(time_left < 0L) {
1349
1352
    failf(data, "timed out before SSL handshake");
1350
1353
    goto error;
1367
1370
  display_conn_info(conn, connssl->handle);
1368
1371
 
1369
1372
  if (data->set.str[STRING_SSL_ISSUERCERT]) {
1370
 
    SECStatus ret;
1371
 
    bool nickname_alloc = FALSE;
1372
 
    char *nickname = fmt_nickname(data->set.str[STRING_SSL_ISSUERCERT],
1373
 
                                  &nickname_alloc);
1374
 
 
 
1373
    SECStatus ret = SECFailure;
 
1374
    bool is_nickname;
 
1375
    char *nickname = fmt_nickname(data, STRING_SSL_ISSUERCERT, &is_nickname);
1375
1376
    if(!nickname)
1376
1377
      return CURLE_OUT_OF_MEMORY;
1377
1378
 
1378
 
    ret = check_issuer_cert(connssl->handle, nickname);
 
1379
    if(is_nickname)
 
1380
      /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */
 
1381
      ret = check_issuer_cert(connssl->handle, nickname);
1379
1382
 
1380
 
    if(nickname_alloc)
1381
 
      free(nickname);
 
1383
    free(nickname);
1382
1384
 
1383
1385
    if(SECFailure == ret) {
1384
1386
      infof(data,"SSL certificate issuer check failed\n");