Coverage Report

Created: 2025-10-10 07:10

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