Coverage Report

Created: 2023-06-07 06:42

/src/net-snmp/snmplib/snmp_openssl.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * snmp_openssl.c
3
 *
4
 * Portions of this file are subject to the following copyright(s).  See
5
 * the Net-SNMP's COPYING file for more details and other copyrights
6
 * that may apply:
7
 *
8
 * Portions of this file are copyrighted by:
9
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
10
 * Use is subject to license terms specified in the COPYING file
11
 * distributed with the Net-SNMP package.
12
 */
13
14
#include <net-snmp/net-snmp-config.h>
15
16
#include <net-snmp/net-snmp-includes.h>
17
18
#include <net-snmp/net-snmp-features.h>
19
20
/** OpenSSL compat functions for apps */
21
#if defined(NETSNMP_USE_OPENSSL)
22
23
#include <string.h>
24
#include <openssl/dh.h>
25
26
#ifndef HAVE_DH_GET0_PQG
27
void
28
DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
29
{
30
   if (p != NULL)
31
       *p = dh->p;
32
   if (q != NULL)
33
       *q = dh->q;
34
   if (g != NULL)
35
       *g = dh->g;
36
}
37
#endif
38
39
#ifndef HAVE_DH_GET0_KEY
40
void
41
DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
42
{
43
   if (pub_key != NULL)
44
       *pub_key = dh->pub_key;
45
   if (priv_key != NULL)
46
       *priv_key = dh->priv_key;
47
}
48
#endif
49
50
#ifndef HAVE_DH_SET0_PQG
51
int
52
DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
53
{
54
   /* If the fields p and g in d are NULL, the corresponding input
55
    * parameters MUST be non-NULL.  q may remain NULL.
56
    */
57
   if ((dh->p == NULL && p == NULL)
58
       || (dh->g == NULL && g == NULL))
59
       return 0;
60
61
   if (p != NULL) {
62
       BN_free(dh->p);
63
       dh->p = p;
64
   }
65
   if (q != NULL) {
66
       BN_free(dh->q);
67
       dh->q = q;
68
   }
69
   if (g != NULL) {
70
       BN_free(dh->g);
71
       dh->g = g;
72
   }
73
74
   if (q != NULL) {
75
       dh->length = BN_num_bits(q);
76
   }
77
78
   return 1;
79
}
80
#endif
81
#endif /* defined(NETSNMP_USE_OPENSSL) */
82
83
/** TLS/DTLS certificatte support */
84
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) && !defined(NETSNMP_FEATURE_REMOVE_CERT_UTIL)
85
86
netsnmp_feature_require(container_free_all);
87
88
netsnmp_feature_child_of(openssl_cert_get_subjectAltNames, netsnmp_unused);
89
netsnmp_feature_child_of(openssl_ht2nid, netsnmp_unused);
90
netsnmp_feature_child_of(openssl_err_log, netsnmp_unused);
91
netsnmp_feature_child_of(cert_dump_names, netsnmp_unused);
92
93
#include <ctype.h>
94
95
#include <openssl/evp.h>
96
#include <openssl/ssl.h>
97
#include <openssl/x509.h>
98
#include <openssl/x509v3.h>
99
#include <openssl/err.h>
100
#include <openssl/objects.h>
101
102
#include <net-snmp/library/snmp_debug.h>
103
#include <net-snmp/library/cert_util.h>
104
#include <net-snmp/library/snmp_openssl.h>
105
106
static u_char have_started_already = 0;
107
108
/*
109
 * This code merely does openssl initialization so that multilpe
110
 * modules are safe to call netsnmp_init_openssl() for bootstrapping
111
 * without worrying about other callers that may have already done so.
112
 */
113
0
void netsnmp_init_openssl(void) {
114
115
    /* avoid duplicate calls */
116
0
    if (have_started_already)
117
0
        return;
118
0
    have_started_already = 1;
119
120
0
    DEBUGMSGTL(("snmp_openssl", "initializing\n"));
121
122
    /* Initializing OpenSSL */
123
0
#ifdef HAVE_SSL_LIBRARY_INIT
124
0
    SSL_library_init();
125
0
#endif
126
0
#ifdef HAVE_SSL_LOAD_ERROR_STRINGS
127
0
    SSL_load_error_strings();
128
0
#endif
129
#ifdef HAVE_ERR_LOAD_BIO_STRINGS
130
    ERR_load_BIO_strings();
131
#endif
132
0
#ifdef HAVE_OPENSSL_ADD_ALL_ALGORITHMS
133
0
    OpenSSL_add_all_algorithms();
134
0
#endif
135
0
}
136
137
/** netsnmp_openssl_cert_get_name: get subject name field from cert
138
 * @internal
139
 */
140
/** instead of exposing this function, make helper functions for each
141
 * field, like netsnmp_openssl_cert_get_commonName, below */
142
static char *
143
_cert_get_name(X509 *ocert, int which, char **buf, int *len, int flags)
144
0
{
145
0
    X509_NAME       *osubj_name;
146
0
    int              space;
147
0
    char            *buf_ptr;
148
149
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
150
0
        return NULL;
151
152
0
    osubj_name = X509_get_subject_name(ocert);
153
0
    if (NULL == osubj_name) {
154
0
        DEBUGMSGT(("openssl:cert:name", "no subject name!\n"));
155
0
        return NULL;
156
0
    }
157
158
    /** see if buf is big enough, or allocate buf if none specified */
159
0
    space = X509_NAME_get_text_by_NID(osubj_name, which, NULL, 0);
160
0
    if (-1 == space)
161
0
        return NULL;
162
0
    ++space; /* for NUL */
163
0
    if (buf && *buf) {
164
0
        if (*len < space)
165
0
            return NULL;
166
0
        buf_ptr = *buf;
167
0
    }
168
0
    else {
169
0
        buf_ptr = calloc(1,space);
170
0
        if (!buf_ptr)
171
0
            return NULL;
172
0
    }
173
0
    space = X509_NAME_get_text_by_NID(osubj_name, which, buf_ptr, space);
174
0
    if (len)
175
0
        *len = space;
176
177
0
    return buf_ptr;
178
0
}
179
180
/** netsnmp_openssl_cert_get_subjectName: get subject name field from cert
181
 */
182
char *
183
netsnmp_openssl_cert_get_subjectName(X509 *ocert, char **buf, int *len)
184
0
{
185
0
    X509_NAME       *osubj_name;
186
0
    int              space;
187
0
    char            *buf_ptr;
188
189
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
190
0
        return NULL;
191
192
0
    osubj_name = X509_get_subject_name(ocert);
193
0
    if (NULL == osubj_name) {
194
0
        DEBUGMSGT(("openssl:cert:name", "no subject name!\n"));
195
0
        return NULL;
196
0
    }
197
198
0
    if (buf) {
199
0
        buf_ptr = *buf;
200
0
        space = *len;
201
0
    }
202
0
    else {
203
0
        buf_ptr = NULL;
204
0
        space = 0;
205
0
    }
206
0
    buf_ptr = X509_NAME_oneline(osubj_name, buf_ptr, space);
207
0
    if (len)
208
0
        *len = strlen(buf_ptr);
209
210
0
    return buf_ptr;
211
0
}
212
213
/** netsnmp_openssl_cert_get_commonName: get commonName for cert.
214
 * if a pointer to a buffer and its length are specified, they will be
215
 * used. otherwise, a new buffer will be allocated, which the caller will
216
 * be responsbile for releasing.
217
 */
218
char *
219
netsnmp_openssl_cert_get_commonName(X509 *ocert, char **buf, int *len)
220
0
{
221
0
    return _cert_get_name(ocert, NID_commonName, buf, len, 0);
222
0
}
223
224
#ifndef NETSNMP_FEATURE_REMOVE_CERT_DUMP_NAMES
225
226
/** netsnmp_openssl_cert_dump_name: dump subject names in cert
227
 */
228
void
229
netsnmp_openssl_cert_dump_names(X509 *ocert)
230
0
{
231
0
    int              i, onid;
232
0
    X509_NAME_ENTRY *oname_entry;
233
0
    ASN1_STRING     *oname_value;
234
0
    X509_NAME       *osubj_name;
235
0
    const char      *prefix_short, *prefix_long;
236
237
0
    if (NULL == ocert)
238
0
        return;
239
240
0
    osubj_name = X509_get_subject_name(ocert);
241
0
    if (NULL == osubj_name) {
242
0
        DEBUGMSGT(("9:cert:dump:names", "no subject name!\n"));
243
0
        return;
244
0
    }
245
246
0
    for (i = 0; i < X509_NAME_entry_count(osubj_name); i++) {
247
0
        oname_entry = X509_NAME_get_entry(osubj_name, i);
248
0
        netsnmp_assert(NULL != oname_entry);
249
0
        oname_value = X509_NAME_ENTRY_get_data(oname_entry);
250
251
0
        if (oname_value->type != V_ASN1_PRINTABLESTRING)
252
0
            continue;
253
254
        /** get NID */
255
0
        onid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(oname_entry));
256
0
        if (onid == NID_undef) {
257
0
            prefix_long = prefix_short = "UNKNOWN";
258
0
        }
259
0
        else {
260
0
            prefix_long = OBJ_nid2ln(onid);
261
0
            prefix_short = OBJ_nid2sn(onid);
262
0
        }
263
264
0
        DEBUGMSGT(("9:cert:dump:names",
265
0
                   "[%02d] NID type %d, ASN type %d\n", i, onid,
266
0
                   oname_value->type));
267
0
        DEBUGMSGT(("9:cert:dump:names", "%s/%s: '%s'\n", prefix_long,
268
0
                   prefix_short, ASN1_STRING_get0_data(oname_value)));
269
0
    }
270
0
}
271
#endif /* NETSNMP_FEATURE_REMOVE_CERT_DUMP_NAMES */
272
273
static char *
274
_cert_get_extension(X509_EXTENSION  *oext, char **buf, int *len, int flags)
275
0
{
276
0
    int              space;
277
0
    char            *buf_ptr = NULL;
278
0
    u_char          *data;
279
0
    BIO             *bio;
280
    
281
0
    if ((NULL == oext) || ((buf && !len) || (len && !buf)))
282
0
        return NULL;
283
284
0
    bio = BIO_new(BIO_s_mem());
285
0
    if (NULL == bio) {
286
0
        snmp_log(LOG_ERR, "could not get bio for extension\n");
287
0
        return NULL;
288
0
    }
289
0
    if (X509V3_EXT_print(bio, oext, 0, 0) != 1) {
290
0
        snmp_log(LOG_ERR, "could not print extension!\n");
291
0
        goto out;
292
0
    }
293
294
0
    space = BIO_get_mem_data(bio, &data);
295
0
    if (buf && *buf) {
296
0
        if (*len < space + 1) {
297
0
            snmp_log(LOG_ERR, "not enough buffer space to print extension\n");
298
0
            goto out;
299
0
        }
300
0
        buf_ptr = *buf;
301
0
    } else {
302
0
        buf_ptr = calloc(1, space + 1);
303
0
    }
304
    
305
0
    if (!buf_ptr) {
306
0
        snmp_log(LOG_ERR, "error in allocation for extension\n");
307
0
        goto out;
308
0
    }
309
0
    memcpy(buf_ptr, data, space);
310
0
    buf_ptr[space] = 0;
311
0
    if (len)
312
0
        *len = space;
313
314
0
out:
315
0
    BIO_vfree(bio);
316
317
0
    return buf_ptr;
318
0
}
319
320
/** netsnmp_openssl_cert_get_extension: get extension field from cert
321
 * @internal
322
 */
323
/** instead of exposing this function, make helper functions for each
324
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
325
X509_EXTENSION  *
326
_cert_get_extension_at(X509 *ocert, int pos, char **buf, int *len, int flags)
327
0
{
328
0
    X509_EXTENSION  *oext;
329
330
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
331
0
        return NULL;
332
333
0
    oext = X509_get_ext(ocert,pos);
334
0
    if (NULL == oext) {
335
0
        snmp_log(LOG_ERR, "extension number %d not found!\n", pos);
336
0
        netsnmp_openssl_cert_dump_extensions(ocert);
337
0
        return NULL;
338
0
    }
339
340
0
    return oext;
341
0
}
342
343
/** netsnmp_openssl_cert_get_extension: get extension field from cert
344
 * @internal
345
 */
346
/** instead of exposing this function, make helper functions for each
347
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
348
static char *
349
_cert_get_extension_str_at(X509 *ocert, int pos, char **buf, int *len,
350
                           int flags)
351
0
{
352
0
    X509_EXTENSION  *oext;
353
354
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
355
0
        return NULL;
356
357
0
    oext = X509_get_ext(ocert,pos);
358
0
    if (NULL == oext) {
359
0
        snmp_log(LOG_ERR, "extension number %d not found!\n", pos);
360
0
        netsnmp_openssl_cert_dump_extensions(ocert);
361
0
        return NULL;
362
0
    }
363
364
0
    return _cert_get_extension(oext, buf, len, flags);
365
0
}
366
367
/** _cert_get_extension_id: get extension field from cert
368
 * @internal
369
 */
370
/** instead of exposing this function, make helper functions for each
371
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
372
X509_EXTENSION *
373
_cert_get_extension_id(X509 *ocert, int which, char **buf, int *len, int flags)
374
0
{
375
0
    int pos;
376
377
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
378
0
        return NULL;
379
380
0
    pos = X509_get_ext_by_NID(ocert,which,-1);
381
0
    if (pos < 0) {
382
0
        DEBUGMSGT(("openssl:cert:name", "no extension %d\n", which));
383
0
        return NULL;
384
0
    }
385
386
0
    return _cert_get_extension_at(ocert, pos, buf, len, flags);
387
0
}
388
389
#ifndef NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES
390
/** _cert_get_extension_id_str: get extension field from cert
391
 * @internal
392
 */
393
/** instead of exposing this function, make helper functions for each
394
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
395
static char *
396
_cert_get_extension_id_str(X509 *ocert, int which, char **buf, int *len,
397
                           int flags)
398
0
{
399
0
    int pos;
400
401
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
402
0
        return NULL;
403
404
0
    pos = X509_get_ext_by_NID(ocert,which,-1);
405
0
    if (pos < 0) {
406
0
        DEBUGMSGT(("openssl:cert:name", "no extension %d\n", which));
407
0
        return NULL;
408
0
    }
409
410
0
    return _cert_get_extension_str_at(ocert, pos, buf, len, flags);
411
0
}
412
#endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES */
413
414
static char *
415
_extract_oname(const GENERAL_NAME *oname)
416
0
{
417
0
    char  ipbuf[60], *buf = NULL, *rtn = NULL;
418
419
0
    if (NULL == oname)
420
0
        return NULL;
421
422
0
    switch ( oname->type ) {
423
0
        case GEN_EMAIL:
424
0
        case GEN_DNS:
425
            /*case GEN_URI:*/
426
0
            ASN1_STRING_to_UTF8((unsigned char**)&buf, oname->d.ia5);
427
0
            if (buf)
428
0
                rtn = strdup(buf);
429
0
            break;
430
431
0
        case GEN_IPADD:
432
0
            if (oname->d.iPAddress->length == 4) {
433
0
                sprintf(ipbuf, "%d.%d.%d.%d", oname->d.iPAddress->data[0],
434
0
                        oname->d.iPAddress->data[1],
435
0
                        oname->d.iPAddress->data[2],
436
0
                        oname->d.iPAddress->data[3]);
437
0
                rtn = strdup(ipbuf);
438
0
            }
439
0
            else if ((oname->d.iPAddress->length == 16) ||
440
0
                     (oname->d.iPAddress->length == 20)) {
441
0
                char *pos = ipbuf;
442
0
                int   j;
443
0
                for(j = 0; j < oname->d.iPAddress->length; ++j) {
444
0
                    *pos++ = VAL2HEX(oname->d.iPAddress->data[j]);
445
0
                    *pos++ = ':';
446
0
                }
447
0
                *pos = '\0';
448
0
                rtn = strdup(ipbuf);
449
0
            }
450
0
            else
451
0
                NETSNMP_LOGONCE((LOG_WARNING, "unexpected ip addr length %d\n",
452
0
                       oname->d.iPAddress->length));
453
454
0
            break;
455
0
        default:
456
0
            DEBUGMSGT(("openssl:cert:san", "unknown/unsupported type %d\n",
457
0
                       oname->type));
458
0
            break;
459
0
    }
460
0
    DEBUGMSGT(("9:openssl:cert:san", "san=%s\n", buf));
461
0
    if (buf)
462
0
        OPENSSL_free(buf);
463
464
0
    return rtn;
465
0
}
466
467
#ifndef NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES
468
/** netsnmp_openssl_cert_get_subjectAltName: get subjectAltName for cert.
469
 * if a pointer to a buffer and its length are specified, they will be
470
 * used. otherwise, a new buffer will be allocated, which the caller will
471
 * be responsbile for releasing.
472
 */
473
char *
474
netsnmp_openssl_cert_get_subjectAltNames(X509 *ocert, char **buf, int *len)
475
0
{
476
0
    return _cert_get_extension_id_str(ocert, NID_subject_alt_name, buf, len, 0);
477
0
}
478
#endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES */
479
480
void
481
netsnmp_openssl_cert_dump_extensions(X509 *ocert)
482
0
{
483
0
    X509_EXTENSION  *extension;
484
0
    const char      *extension_name;
485
0
    char             buf[SNMP_MAXBUF], *buf_ptr = buf, *str, *lf;
486
0
    int              i, num_extensions, buf_len, nid;
487
488
0
    if (NULL == ocert)
489
0
        return;
490
491
0
    DEBUGIF("9:cert:dump") 
492
0
        ;
493
0
    else
494
0
        return; /* bail if debug not enabled */
495
496
0
    num_extensions = X509_get_ext_count(ocert);
497
0
    if (0 == num_extensions)
498
0
        DEBUGMSGT(("9:cert:dump", "    0 extensions\n"));
499
0
    for(i = 0; i < num_extensions; i++) {
500
0
        extension = X509_get_ext(ocert, i);
501
0
        nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
502
0
        extension_name = OBJ_nid2sn(nid);
503
0
        buf_len = sizeof(buf);
504
0
        str = _cert_get_extension_str_at(ocert, i, &buf_ptr, &buf_len, 0);
505
0
        if (!str) {
506
0
            DEBUGMSGT(("9:cert:dump", "    %2d: %s\n", i,
507
0
                        extension_name));
508
0
            continue;
509
0
        }
510
0
        lf = strchr(str, '\n'); /* look for multiline strings */
511
0
        if (NULL != lf)
512
0
            *lf = '\0'; /* only log first line of multiline here */
513
0
        DEBUGMSGT(("9:cert:dump", "    %2d: %s = %s\n", i,
514
0
                   extension_name, str));
515
0
        while(lf) { /* log remaining parts of multiline string */
516
0
            str = ++lf;
517
0
            if (*str == '\0')
518
0
               break;
519
0
            lf = strchr(str, '\n');
520
0
            if (NULL == lf) 
521
0
                break;
522
0
            *lf = '\0';
523
0
            DEBUGMSGT(("9:cert:dump", "        %s\n", str));
524
0
        }
525
0
    }
526
0
}
527
528
static const struct {
529
    uint16_t nid;
530
    uint16_t ht;
531
} _htmap[] = {
532
    { 0, NS_HASH_NONE },
533
#ifdef NID_md5WithRSAEncryption
534
    { NID_md5WithRSAEncryption, NS_HASH_MD5 },
535
#endif
536
#ifdef NID_sha1WithRSAEncryption
537
    { NID_sha1WithRSAEncryption, NS_HASH_SHA1 },
538
#endif
539
#ifdef NID_ecdsa_with_SHA1
540
    { NID_ecdsa_with_SHA1, NS_HASH_SHA1 },
541
#endif
542
#ifdef NID_sha224WithRSAEncryption
543
    { NID_sha224WithRSAEncryption, NS_HASH_SHA224 },
544
#endif
545
#ifdef NID_ecdsa_with_SHA224
546
    { NID_ecdsa_with_SHA224, NS_HASH_SHA224 },
547
#endif
548
#ifdef NID_sha256WithRSAEncryption
549
    { NID_sha256WithRSAEncryption, NS_HASH_SHA256 },
550
#endif
551
#ifdef NID_ecdsa_with_SHA256
552
    { NID_ecdsa_with_SHA256, NS_HASH_SHA256 },
553
#endif
554
#ifdef NID_sha384WithRSAEncryption
555
    { NID_sha384WithRSAEncryption, NS_HASH_SHA384 },
556
#endif
557
#ifdef NID_ecdsa_with_SHA384
558
    { NID_ecdsa_with_SHA384, NS_HASH_SHA384 },
559
#endif
560
#ifdef NID_sha512WithRSAEncryption
561
    { NID_sha512WithRSAEncryption, NS_HASH_SHA512 },
562
#endif
563
#ifdef NID_ecdsa_with_SHA512
564
    { NID_ecdsa_with_SHA512, NS_HASH_SHA512 },
565
#endif
566
};
567
568
int
569
_nid2ht(int nid)
570
0
{
571
0
    int i;
572
573
0
    for (i = 0; i < sizeof(_htmap) / sizeof(_htmap[0]); i++) {
574
0
        if (_htmap[i].nid == nid)
575
0
            return _htmap[i].ht;
576
0
    }
577
0
    return 0;
578
0
}
579
580
#ifndef NETSNMP_FEATURE_REMOVE_OPENSSL_HT2NID
581
int
582
_ht2nid(int ht)
583
0
{
584
0
    int i;
585
586
0
    for (i = 0; i < sizeof(_htmap) / sizeof(_htmap[0]); i++) {
587
0
        if (_htmap[i].ht == ht)
588
0
            return _htmap[i].nid;
589
0
    }
590
0
    return 0;
591
0
}
592
#endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_HT2NID */
593
594
/**
595
 * returns allocated pointer caller must free.
596
 */
597
int
598
netsnmp_openssl_cert_get_hash_type(X509 *ocert)
599
0
{
600
0
    if (NULL == ocert)
601
0
        return 0;
602
603
0
    return _nid2ht(X509_get_signature_nid(ocert));
604
0
}
605
606
/**
607
 * returns allocated pointer caller must free.
608
 */
609
char *
610
netsnmp_openssl_cert_get_fingerprint(X509 *ocert, int alg)
611
0
{
612
0
    u_char           fingerprint[EVP_MAX_MD_SIZE];
613
0
    u_int            fingerprint_len, nid;
614
0
    const EVP_MD    *digest;
615
0
    char            *result = NULL;
616
617
0
    if (NULL == ocert)
618
0
        return NULL;
619
620
0
    nid = X509_get_signature_nid(ocert);
621
0
    DEBUGMSGT(("9:openssl:fingerprint", "alg %d, cert nid %d (%d)\n", alg, nid,
622
0
               _nid2ht(nid)));
623
        
624
0
    if ((-1 == alg) && nid)
625
0
        alg = _nid2ht(nid);
626
627
0
    switch (alg) {
628
0
        case NS_HASH_MD5:
629
0
            snmp_log(LOG_ERR, "hash type md5 not yet supported\n");
630
0
            return NULL;
631
0
            break;
632
        
633
0
        case NS_HASH_NONE:
634
0
            snmp_log(LOG_ERR, "hash type none not supported. using SHA1\n");
635
0
            NETSNMP_FALLTHROUGH;
636
637
0
        case NS_HASH_SHA1:
638
0
            digest = EVP_sha1();
639
0
            break;
640
641
0
#ifdef HAVE_EVP_SHA224
642
0
        case NS_HASH_SHA224:
643
0
            digest = EVP_sha224();
644
0
            break;
645
646
0
        case NS_HASH_SHA256:
647
0
            digest = EVP_sha256();
648
0
            break;
649
650
0
#endif
651
0
#ifdef HAVE_EVP_SHA384
652
0
        case NS_HASH_SHA384:
653
0
            digest = EVP_sha384();
654
0
            break;
655
656
0
        case NS_HASH_SHA512:
657
0
            digest = EVP_sha512();
658
0
            break;
659
0
#endif
660
661
0
        default:
662
0
            snmp_log(LOG_ERR, "unknown hash algorithm %d\n", alg);
663
0
            return NULL;
664
0
    }
665
666
0
    if (_nid2ht(nid) != alg) {
667
0
        DEBUGMSGT(("openssl:fingerprint",
668
0
                   "WARNING: alg %d does not match cert alg %d\n",
669
0
                   alg, _nid2ht(nid)));
670
0
    }
671
0
    if (X509_digest(ocert,digest,fingerprint,&fingerprint_len)) {
672
0
        binary_to_hex(fingerprint, fingerprint_len, &result);
673
0
        if (NULL == result)
674
0
            snmp_log(LOG_ERR, "failed to hexify fingerprint\n");
675
0
        else
676
0
            DEBUGMSGT(("9:openssl:fingerprint", "fingerprint %s\n", result));
677
0
    }
678
0
    else
679
0
        snmp_log(LOG_ERR,"failed to compute fingerprint\n");
680
681
0
    return result;
682
0
}
683
684
/**
685
 * get container of netsnmp_cert_map structures from an ssl connection
686
 * certificate chain.
687
 */
688
netsnmp_container *
689
netsnmp_openssl_get_cert_chain(SSL *ssl)
690
0
{
691
0
    X509                  *ocert, *ocert_tmp;
692
0
    STACK_OF(X509)        *ochain;
693
0
    char                  *fingerprint;
694
0
    netsnmp_container     *chain_map;
695
0
    netsnmp_cert_map      *cert_map;
696
0
    int                    i, sk_num_res;
697
698
0
    netsnmp_assert_or_return(ssl != NULL, NULL);
699
700
0
    ocert = SSL_get_peer_certificate(ssl);
701
0
    if (!ocert) {
702
        /** no peer cert */
703
0
        snmp_log(LOG_ERR, "SSL peer has no certificate\n");
704
0
        return NULL;
705
0
    }
706
0
    DEBUGIF("9:cert:dump") {
707
0
        netsnmp_openssl_cert_dump_extensions(ocert);
708
0
    }
709
710
    /*
711
     * get fingerprint and save it
712
     */
713
0
    fingerprint = netsnmp_openssl_cert_get_fingerprint(ocert, -1);
714
0
    if (NULL == fingerprint)
715
0
        return NULL;
716
717
    /*
718
     * allocate cert map. Don't pass in fingerprint, since it would strdup
719
     * it and we've already got a copy.
720
     */
721
0
    cert_map = netsnmp_cert_map_alloc(NULL, ocert);
722
0
    if (NULL == cert_map) {
723
0
        free(fingerprint);
724
0
        return NULL;
725
0
    }
726
0
    cert_map->fingerprint = fingerprint;
727
0
    cert_map->hashType = netsnmp_openssl_cert_get_hash_type(ocert);
728
729
0
    chain_map = netsnmp_cert_map_container_create(0); /* no fp subcontainer */
730
0
    if (NULL == chain_map) {
731
0
        netsnmp_cert_map_free(cert_map);
732
0
        return NULL;
733
0
    }
734
    
735
0
    CONTAINER_INSERT(chain_map, cert_map);
736
737
    /** check for a chain to a CA */
738
0
    ochain = SSL_get_peer_cert_chain(ssl);
739
0
    sk_num_res = sk_X509_num(ochain);
740
0
    if (!ochain || sk_num_res == 0) {
741
0
        DEBUGMSGT(("ssl:cert:chain", "peer has no cert chain\n"));
742
0
    }
743
0
    else {
744
        /*
745
         * loop over chain, adding fingerprint / cert for each
746
         */
747
0
        DEBUGMSGT(("ssl:cert:chain", "examining cert chain\n"));
748
0
        sk_num_res = sk_X509_num(ochain);
749
0
        for(i = 0; i < sk_num_res; ++i) {
750
0
            ocert_tmp = sk_X509_value(ochain, i);
751
0
            fingerprint = netsnmp_openssl_cert_get_fingerprint(ocert_tmp, -1);
752
0
            if (NULL == fingerprint)
753
0
                break;
754
0
            cert_map = netsnmp_cert_map_alloc(NULL, ocert);
755
0
            if (NULL == cert_map) {
756
0
                free(fingerprint);
757
0
                break;
758
0
            }
759
0
            cert_map->fingerprint = fingerprint;
760
0
            cert_map->hashType = netsnmp_openssl_cert_get_hash_type(ocert_tmp);
761
762
0
            CONTAINER_INSERT(chain_map, cert_map);
763
0
        } /* chain loop */
764
        /*
765
         * if we broke out of loop before finishing, clean up
766
         */
767
0
        if (i < sk_num_res)
768
0
            CONTAINER_FREE_ALL(chain_map, NULL);
769
0
    } /* got peer chain */
770
771
0
    DEBUGMSGT(("ssl:cert:chain", "found %" NETSNMP_PRIz "u certs in chain\n",
772
0
               CONTAINER_SIZE(chain_map)));
773
0
    if (CONTAINER_SIZE(chain_map) == 0) {
774
0
        CONTAINER_FREE(chain_map);
775
0
        chain_map = NULL;
776
0
    }
777
778
0
    return chain_map;
779
0
}
780
781
/*
782
tlstmCertSANRFC822Name "Maps a subjectAltName's rfc822Name to a
783
                  tmSecurityName.  The local part of the rfc822Name is
784
                  passed unaltered but the host-part of the name must
785
                  be passed in lower case.
786
                  Example rfc822Name Field:  FooBar@Example.COM
787
                  is mapped to tmSecurityName: FooBar@example.com"
788
789
tlstmCertSANDNSName "Maps a subjectAltName's dNSName to a
790
                  tmSecurityName after first converting it to all
791
                  lower case."
792
793
tlstmCertSANIpAddress "Maps a subjectAltName's iPAddress to a
794
                  tmSecurityName by transforming the binary encoded
795
                  address as follows:
796
                  1) for IPv4 the value is converted into a decimal
797
                     dotted quad address (e.g. '192.0.2.1')
798
                  2) for IPv6 addresses the value is converted into a
799
                     32-character all lowercase hexadecimal string
800
                     without any colon separators.
801
802
                     Note that the resulting length is the maximum
803
                     length supported by the View-Based Access Control
804
                     Model (VACM).  Note that using both the Transport
805
                     Security Model's support for transport prefixes
806
                     (see the SNMP-TSM-MIB's
807
                     snmpTsmConfigurationUsePrefix object for details)
808
                     will result in securityName lengths that exceed
809
                     what VACM can handle."
810
811
tlstmCertSANAny "Maps any of the following fields using the
812
                  corresponding mapping algorithms:
813
                  | rfc822Name | tlstmCertSANRFC822Name |
814
                  | dNSName    | tlstmCertSANDNSName    |
815
                  | iPAddress  | tlstmCertSANIpAddress  |
816
                  The first matching subjectAltName value found in the
817
                  certificate of the above types MUST be used when
818
                  deriving the tmSecurityName."
819
*/
820
char *
821
_cert_get_san_type(X509 *ocert, int mapType)
822
0
{
823
0
    GENERAL_NAMES      *onames;
824
0
    const GENERAL_NAME *oname = NULL;
825
0
    char               *buf = NULL, *lower = NULL;
826
0
    int                 count, i;
827
 
828
0
    onames = (GENERAL_NAMES *)X509_get_ext_d2i(ocert, NID_subject_alt_name,
829
0
                                               NULL, NULL );
830
0
    if (NULL == onames)
831
0
        return NULL;
832
833
0
    count = sk_GENERAL_NAME_num(onames);
834
835
0
    for (i=0 ; i <count; ++i)  {
836
0
        oname = sk_GENERAL_NAME_value(onames, i);
837
838
0
        if (GEN_DNS == oname->type) {
839
0
            if ((TSNM_tlstmCertSANDNSName == mapType) ||
840
0
                (TSNM_tlstmCertSANAny == mapType)) {
841
0
                lower = buf = _extract_oname( oname );
842
0
                break;
843
0
            }
844
0
        }
845
0
        else if (GEN_IPADD == oname->type) {
846
0
            if ((TSNM_tlstmCertSANIpAddress == mapType) ||
847
0
                (TSNM_tlstmCertSANAny == mapType)) {
848
0
                buf = _extract_oname(oname);
849
0
                break;
850
0
            }
851
0
        }
852
0
        else if (GEN_EMAIL == oname->type) {
853
0
            if ((TSNM_tlstmCertSANRFC822Name == mapType) ||
854
0
                (TSNM_tlstmCertSANAny == mapType)) {
855
0
                buf = _extract_oname(oname);
856
0
                lower = strchr(buf, '@');
857
0
                if (NULL == lower) {
858
0
                    DEBUGMSGT(("openssl:secname:extract",
859
0
                               "email %s has no '@'!\n", buf));
860
0
                }
861
0
                else {
862
0
                    ++lower;
863
0
                    break;
864
0
                }
865
0
            }
866
            
867
0
        }
868
0
    } /* for loop */
869
870
0
    if (lower)
871
0
        for ( ; *lower; ++lower )
872
0
            *lower = tolower(0xFF & *lower);
873
0
    DEBUGMSGT(("openssl:cert:extension:san", "#%d type %d: %s\n", i,
874
0
               oname ? oname->type : -1, buf ? buf : "NULL"));
875
876
0
    return buf;
877
0
}
878
879
char *
880
netsnmp_openssl_extract_secname(netsnmp_cert_map *cert_map,
881
                                netsnmp_cert_map *peer_cert)
882
0
{
883
0
    char       *rtn = NULL;
884
885
0
    if (NULL == cert_map)
886
0
        return NULL;
887
888
0
    DEBUGMSGT(("openssl:secname:extract",
889
0
               "checking priority %d, san of type %d for %s\n",
890
0
               cert_map->priority, cert_map->mapType, peer_cert->fingerprint));
891
892
0
    switch(cert_map->mapType) {
893
0
        case TSNM_tlstmCertSpecified:
894
0
            rtn = strdup(cert_map->data);
895
0
            break;
896
897
0
        case TSNM_tlstmCertSANRFC822Name:
898
0
        case TSNM_tlstmCertSANDNSName:
899
0
        case TSNM_tlstmCertSANIpAddress:
900
0
        case TSNM_tlstmCertSANAny:
901
0
            if (NULL == peer_cert) {
902
0
                DEBUGMSGT(("openssl:secname:extract", "no peer cert for %s\n",
903
0
                           cert_map->fingerprint));
904
0
                break;
905
0
            }
906
0
            rtn = _cert_get_san_type(peer_cert->ocert, cert_map->mapType);
907
0
            if (NULL == rtn) {
908
0
                DEBUGMSGT(("openssl:secname:extract", "no san for %s\n",
909
0
                           peer_cert->fingerprint));
910
0
            }
911
0
            break;
912
913
0
        case TSNM_tlstmCertCommonName:
914
0
            rtn = netsnmp_openssl_cert_get_commonName(cert_map->ocert, NULL,
915
0
                                                       NULL);
916
0
            break;
917
0
        default:
918
0
            snmp_log(LOG_ERR, "cant extract secname for unknown map type %d\n",
919
0
                     cert_map->mapType);
920
0
            break;
921
0
    } /* switch mapType */
922
923
0
    if (rtn) {
924
0
        DEBUGMSGT(("openssl:secname:extract",
925
0
                   "found map %d, type %d for %s: %s\n", cert_map->priority,
926
0
                   cert_map->mapType, peer_cert->fingerprint, rtn));
927
0
        if (strlen(rtn) >32) {
928
0
            DEBUGMSGT(("openssl:secname:extract",
929
0
                       "secName longer than 32 chars! dropping...\n"));
930
0
            SNMP_FREE(rtn);
931
0
        }
932
0
    }
933
0
    else
934
0
        DEBUGMSGT(("openssl:secname:extract",
935
0
                   "no map of type %d for %s\n",
936
0
                   cert_map->mapType, peer_cert->fingerprint));
937
0
    return rtn;
938
0
}
939
940
int
941
netsnmp_openssl_cert_issued_by(X509 *issuer, X509 *cert)
942
0
{
943
0
    return (X509_check_issued(issuer, cert) == X509_V_OK);
944
0
}
945
946
947
void
948
netsnmp_openssl_null_checks(SSL *ssl, int *null_auth, int *null_cipher)
949
0
{
950
0
    const SSL_CIPHER *cipher;
951
0
    char           tmp_buf[128], *cipher_alg, *auth_alg;
952
953
0
    if (null_auth)
954
0
        *null_auth = -1; /* unknown */
955
0
    if (null_cipher)
956
0
        *null_cipher = -1; /* unknown */
957
0
    if (NULL == ssl)
958
0
        return;
959
960
0
    cipher = SSL_get_current_cipher(ssl);
961
0
    if (NULL == cipher) {
962
0
        DEBUGMSGTL(("ssl:cipher", "no cipher yet\n"));
963
0
        return;
964
0
    }
965
0
    SSL_CIPHER_description(NETSNMP_REMOVE_CONST(SSL_CIPHER *, cipher), tmp_buf, sizeof(tmp_buf));
966
    /** no \n since tmp_buf already has one */
967
0
    DEBUGMSGTL(("ssl:cipher", "current cipher: %s", tmp_buf));
968
969
    /*
970
     * run "openssl ciphers -v eNULL" and "openssl ciphers -v aNULL"
971
     * to see NULL encryption/authentication algorithms. e.g.
972
     *
973
     * EXP-ADH-RC4-MD5 SSLv3 Kx=DH(512) Au=None Enc=RC4(40) Mac=MD5  export
974
     * NULL-SHA        SSLv3 Kx=RSA     Au=RSA  Enc=None    Mac=SHA1
975
     */
976
0
    if (null_cipher) {
977
0
        cipher_alg = strstr(tmp_buf, "Enc=");
978
0
        if (cipher_alg) {
979
0
            cipher_alg += 4;
980
0
            if (strncmp(cipher_alg,"None", 4) == 0)
981
0
                *null_cipher = 1;
982
0
            else
983
0
                *null_cipher = 0;
984
0
        }
985
0
    }
986
0
    if (null_auth) {
987
0
        auth_alg = strstr(tmp_buf, "Au=");
988
0
        if (auth_alg) {
989
0
            auth_alg += 3;
990
0
            if (strncmp(auth_alg,"None", 4) == 0)
991
0
                *null_auth = 1;
992
0
            else
993
0
                *null_auth = 0;
994
0
        }
995
0
    }
996
0
}
997
998
#ifndef HAVE_X509_GET_SIGNATURE_NID
999
int X509_get_signature_nid(const X509 *x)
1000
{
1001
    return OBJ_obj2nid(x->sig_alg->algorithm);
1002
}
1003
#endif
1004
1005
#ifndef HAVE_ASN1_STRING_GET0_DATA
1006
const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
1007
{
1008
    return x->data;
1009
}
1010
#endif
1011
1012
#ifndef HAVE_X509_NAME_ENTRY_GET_OBJECT
1013
ASN1_OBJECT *X509_NAME_ENTRY_get_object(const X509_NAME_ENTRY *ne)
1014
{
1015
    if (ne == NULL)
1016
        return NULL;
1017
    return ne->object;
1018
}
1019
#endif
1020
1021
#ifndef HAVE_X509_NAME_ENTRY_GET_DATA
1022
ASN1_STRING *X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne)
1023
{
1024
    if (ne == NULL)
1025
        return NULL;
1026
    return ne->value;
1027
}
1028
#endif
1029
1030
#ifndef HAVE_TLS_METHOD
1031
const SSL_METHOD *TLS_method(void)
1032
{
1033
    return TLSv1_method();
1034
}
1035
#endif
1036
1037
#ifndef HAVE_DTLS_METHOD
1038
const SSL_METHOD *DTLS_method(void)
1039
{
1040
    return DTLSv1_method();
1041
}
1042
#endif
1043
1044
#endif /* NETSNMP_USE_OPENSSL && HAVE_LIBSSL && !defined(NETSNMP_FEATURE_REMOVE_CERT_UTIL) */