Coverage Report

Created: 2026-07-16 06:27

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
0
void netsnmp_init_openssl(void) {
119
120
    /* avoid duplicate calls */
121
0
    if (have_started_already)
122
0
        return;
123
0
    have_started_already = 1;
124
125
0
    DEBUGMSGTL(("snmp_openssl", "initializing\n"));
126
127
    /* Initializing OpenSSL */
128
0
#ifdef HAVE_SSL_LIBRARY_INIT
129
0
    SSL_library_init();
130
0
#endif
131
0
#ifdef HAVE_SSL_LOAD_ERROR_STRINGS
132
0
    SSL_load_error_strings();
133
0
#endif
134
#ifdef HAVE_ERR_LOAD_BIO_STRINGS
135
    ERR_load_BIO_strings();
136
#endif
137
0
#ifdef HAVE_OPENSSL_ADD_ALL_ALGORITHMS
138
0
    OpenSSL_add_all_algorithms();
139
0
#endif
140
0
}
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
    int              oname_value_type;
238
0
    X509_NAME_ENTRY *oname_entry;
239
0
    ASN1_STRING     *oname_value;
240
0
    X509_NAME       *osubj_name;
241
0
    const char      *prefix_short, *prefix_long;
242
243
0
    if (NULL == ocert)
244
0
        return;
245
246
0
    osubj_name = X509_get_subject_name(ocert);
247
0
    if (NULL == osubj_name) {
248
0
        DEBUGMSGT(("9:cert:dump:names", "no subject name!\n"));
249
0
        return;
250
0
    }
251
252
0
    for (i = 0; i < X509_NAME_entry_count(osubj_name); i++) {
253
0
        oname_entry = X509_NAME_get_entry(osubj_name, i);
254
0
        netsnmp_assert(NULL != oname_entry);
255
0
        oname_value = X509_NAME_ENTRY_get_data(oname_entry);
256
0
        oname_value_type = ASN1_STRING_type(oname_value);
257
258
0
        if (oname_value_type != V_ASN1_PRINTABLESTRING)
259
0
            continue;
260
261
        /** get NID */
262
0
        onid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(oname_entry));
263
0
        if (onid == NID_undef) {
264
0
            prefix_long = prefix_short = "UNKNOWN";
265
0
        }
266
0
        else {
267
0
            prefix_long = OBJ_nid2ln(onid);
268
0
            prefix_short = OBJ_nid2sn(onid);
269
0
        }
270
271
0
        DEBUGMSGT(("9:cert:dump:names",
272
0
                   "[%02d] NID type %d, ASN type %d\n", i, onid,
273
0
                   oname_value_type));
274
0
        DEBUGMSGT(("9:cert:dump:names", "%s/%s: '%s'\n", prefix_long,
275
0
                   prefix_short, ASN1_STRING_get0_data(oname_value)));
276
0
    }
277
0
}
278
#endif /* NETSNMP_FEATURE_REMOVE_CERT_DUMP_NAMES */
279
280
static char *
281
_cert_get_extension(X509_EXTENSION  *oext, char **buf, int *len, int flags)
282
0
{
283
0
    int              space;
284
0
    char            *buf_ptr = NULL;
285
0
    u_char          *data;
286
0
    BIO             *bio;
287
    
288
0
    if ((NULL == oext) || ((buf && !len) || (len && !buf)))
289
0
        return NULL;
290
291
0
    bio = BIO_new(BIO_s_mem());
292
0
    if (NULL == bio) {
293
0
        snmp_log(LOG_ERR, "could not get bio for extension\n");
294
0
        return NULL;
295
0
    }
296
0
    if (X509V3_EXT_print(bio, oext, 0, 0) != 1) {
297
0
        snmp_log(LOG_ERR, "could not print extension!\n");
298
0
        goto out;
299
0
    }
300
301
0
    space = BIO_get_mem_data(bio, &data);
302
0
    if (buf && *buf) {
303
0
        if (*len < space + 1) {
304
0
            snmp_log(LOG_ERR, "not enough buffer space to print extension\n");
305
0
            goto out;
306
0
        }
307
0
        buf_ptr = *buf;
308
0
    } else {
309
0
        buf_ptr = calloc(1, space + 1);
310
0
    }
311
    
312
0
    if (!buf_ptr) {
313
0
        snmp_log(LOG_ERR, "error in allocation for extension\n");
314
0
        goto out;
315
0
    }
316
0
    memcpy(buf_ptr, data, space);
317
0
    buf_ptr[space] = 0;
318
0
    if (len)
319
0
        *len = space;
320
321
0
out:
322
0
    BIO_vfree(bio);
323
324
0
    return buf_ptr;
325
0
}
326
327
/** netsnmp_openssl_cert_get_extension: get extension field from cert
328
 * @internal
329
 */
330
/** instead of exposing this function, make helper functions for each
331
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
332
X509_EXTENSION  *
333
_cert_get_extension_at(X509 *ocert, int pos, char **buf, int *len, int flags)
334
0
{
335
0
    X509_EXTENSION  *oext;
336
337
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
338
0
        return NULL;
339
340
0
    oext = X509_get_ext(ocert,pos);
341
0
    if (NULL == oext) {
342
0
        snmp_log(LOG_ERR, "extension number %d not found!\n", pos);
343
0
        netsnmp_openssl_cert_dump_extensions(ocert);
344
0
        return NULL;
345
0
    }
346
347
0
    return oext;
348
0
}
349
350
/** netsnmp_openssl_cert_get_extension: get extension field from cert
351
 * @internal
352
 */
353
/** instead of exposing this function, make helper functions for each
354
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
355
static char *
356
_cert_get_extension_str_at(X509 *ocert, int pos, char **buf, int *len,
357
                           int flags)
358
0
{
359
0
    X509_EXTENSION  *oext;
360
361
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
362
0
        return NULL;
363
364
0
    oext = X509_get_ext(ocert,pos);
365
0
    if (NULL == oext) {
366
0
        snmp_log(LOG_ERR, "extension number %d not found!\n", pos);
367
0
        netsnmp_openssl_cert_dump_extensions(ocert);
368
0
        return NULL;
369
0
    }
370
371
0
    return _cert_get_extension(oext, buf, len, flags);
372
0
}
373
374
/** _cert_get_extension_id: get extension field from cert
375
 * @internal
376
 */
377
/** instead of exposing this function, make helper functions for each
378
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
379
X509_EXTENSION *
380
_cert_get_extension_id(X509 *ocert, int which, char **buf, int *len, int flags)
381
0
{
382
0
    int pos;
383
384
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
385
0
        return NULL;
386
387
0
    pos = X509_get_ext_by_NID(ocert,which,-1);
388
0
    if (pos < 0) {
389
0
        DEBUGMSGT(("openssl:cert:name", "no extension %d\n", which));
390
0
        return NULL;
391
0
    }
392
393
0
    return _cert_get_extension_at(ocert, pos, buf, len, flags);
394
0
}
395
396
#ifndef NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES
397
/** _cert_get_extension_id_str: get extension field from cert
398
 * @internal
399
 */
400
/** instead of exposing this function, make helper functions for each
401
 * field, like netsnmp_openssl_cert_get_subjectAltName, below */
402
static char *
403
_cert_get_extension_id_str(X509 *ocert, int which, char **buf, int *len,
404
                           int flags)
405
0
{
406
0
    int pos;
407
408
0
    if ((NULL == ocert) || ((buf && !len) || (len && !buf)))
409
0
        return NULL;
410
411
0
    pos = X509_get_ext_by_NID(ocert,which,-1);
412
0
    if (pos < 0) {
413
0
        DEBUGMSGT(("openssl:cert:name", "no extension %d\n", which));
414
0
        return NULL;
415
0
    }
416
417
0
    return _cert_get_extension_str_at(ocert, pos, buf, len, flags);
418
0
}
419
#endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES */
420
421
static char *
422
_extract_oname(const GENERAL_NAME *oname)
423
0
{
424
0
    char  ipbuf[60], *buf = NULL, *rtn = NULL;
425
426
0
    if (NULL == oname)
427
0
        return NULL;
428
429
0
    switch ( oname->type ) {
430
0
        case GEN_EMAIL:
431
0
        case GEN_DNS:
432
            /*case GEN_URI:*/
433
0
            ASN1_STRING_to_UTF8((unsigned char**)&buf, oname->d.ia5);
434
0
            if (buf)
435
0
                rtn = strdup(buf);
436
0
            break;
437
438
0
        case GEN_IPADD: {
439
0
            const int iplen = ASN1_STRING_length(oname->d.iPAddress);
440
0
            const unsigned char *const ipdata =
441
0
                ASN1_STRING_get0_data(oname->d.iPAddress);
442
443
0
            if (iplen == 4) {
444
0
                sprintf(ipbuf, "%d.%d.%d.%d", ipdata[0],
445
0
                        ipdata[1],
446
0
                        ipdata[2],
447
0
                        ipdata[3]);
448
0
                rtn = strdup(ipbuf);
449
0
            } else if (iplen == 16 || iplen == 20) {
450
0
                char *pos = ipbuf;
451
0
                int   j;
452
453
0
                for (j = 0; j < iplen; ++j) {
454
0
                    *pos++ = VAL2HEX(ipdata[j]);
455
0
                    *pos++ = ':';
456
0
                }
457
0
                *pos = '\0';
458
0
                rtn = strdup(ipbuf);
459
0
            } else {
460
0
                NETSNMP_LOGONCE((LOG_WARNING, "unexpected ip addr length %d\n",
461
0
                       iplen));
462
0
            }
463
0
            break;
464
0
        }
465
0
        default:
466
0
            DEBUGMSGT(("openssl:cert:san", "unknown/unsupported type %d\n",
467
0
                       oname->type));
468
0
            break;
469
0
    }
470
0
    DEBUGMSGT(("9:openssl:cert:san", "san=%s\n", buf));
471
0
    if (buf)
472
0
        OPENSSL_free(buf);
473
474
0
    return rtn;
475
0
}
476
477
#ifndef NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES
478
/** netsnmp_openssl_cert_get_subjectAltName: get subjectAltName for cert.
479
 * if a pointer to a buffer and its length are specified, they will be
480
 * used. otherwise, a new buffer will be allocated, which the caller will
481
 * be responsible for releasing.
482
 */
483
char *
484
netsnmp_openssl_cert_get_subjectAltNames(X509 *ocert, char **buf, int *len)
485
0
{
486
0
    return _cert_get_extension_id_str(ocert, NID_subject_alt_name, buf, len, 0);
487
0
}
488
#endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_CERT_GET_SUBJECTALTNAMES */
489
490
void
491
netsnmp_openssl_cert_dump_extensions(X509 *ocert)
492
0
{
493
0
    X509_EXTENSION  *extension;
494
0
    const char      *extension_name;
495
0
    char             buf[SNMP_MAXBUF], *buf_ptr = buf, *str, *lf;
496
0
    int              i, num_extensions, buf_len, nid;
497
498
0
    if (NULL == ocert)
499
0
        return;
500
501
0
    DEBUGIF("9:cert:dump") 
502
0
        ;
503
0
    else
504
0
        return; /* bail if debug not enabled */
505
506
0
    num_extensions = X509_get_ext_count(ocert);
507
0
    if (0 == num_extensions)
508
0
        DEBUGMSGT(("9:cert:dump", "    0 extensions\n"));
509
0
    for(i = 0; i < num_extensions; i++) {
510
0
        extension = X509_get_ext(ocert, i);
511
0
        nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
512
0
        extension_name = OBJ_nid2sn(nid);
513
0
        buf_len = sizeof(buf);
514
0
        str = _cert_get_extension_str_at(ocert, i, &buf_ptr, &buf_len, 0);
515
0
        if (!str) {
516
0
            DEBUGMSGT(("9:cert:dump", "    %2d: %s\n", i,
517
0
                        extension_name));
518
0
            continue;
519
0
        }
520
0
        lf = strchr(str, '\n'); /* look for multiline strings */
521
0
        if (NULL != lf)
522
0
            *lf = '\0'; /* only log first line of multiline here */
523
0
        DEBUGMSGT(("9:cert:dump", "    %2d: %s = %s\n", i,
524
0
                   extension_name, str));
525
0
        while(lf) { /* log remaining parts of multiline string */
526
0
            str = ++lf;
527
0
            if (*str == '\0')
528
0
               break;
529
0
            lf = strchr(str, '\n');
530
0
            if (NULL == lf) 
531
0
                break;
532
0
            *lf = '\0';
533
0
            DEBUGMSGT(("9:cert:dump", "        %s\n", str));
534
0
        }
535
0
    }
536
0
}
537
538
static const struct {
539
    uint16_t nid;
540
    uint16_t ht;
541
} _htmap[] = {
542
    { 0, NS_HASH_NONE },
543
#ifdef NID_md5WithRSAEncryption
544
    { NID_md5WithRSAEncryption, NS_HASH_MD5 },
545
#endif
546
#ifdef NID_sha1WithRSAEncryption
547
    { NID_sha1WithRSAEncryption, NS_HASH_SHA1 },
548
#endif
549
#ifdef NID_ecdsa_with_SHA1
550
    { NID_ecdsa_with_SHA1, NS_HASH_SHA1 },
551
#endif
552
#ifdef NID_sha224WithRSAEncryption
553
    { NID_sha224WithRSAEncryption, NS_HASH_SHA224 },
554
#endif
555
#ifdef NID_ecdsa_with_SHA224
556
    { NID_ecdsa_with_SHA224, NS_HASH_SHA224 },
557
#endif
558
#ifdef NID_sha256WithRSAEncryption
559
    { NID_sha256WithRSAEncryption, NS_HASH_SHA256 },
560
#endif
561
#ifdef NID_ecdsa_with_SHA256
562
    { NID_ecdsa_with_SHA256, NS_HASH_SHA256 },
563
#endif
564
#ifdef NID_sha384WithRSAEncryption
565
    { NID_sha384WithRSAEncryption, NS_HASH_SHA384 },
566
#endif
567
#ifdef NID_ecdsa_with_SHA384
568
    { NID_ecdsa_with_SHA384, NS_HASH_SHA384 },
569
#endif
570
#ifdef NID_sha512WithRSAEncryption
571
    { NID_sha512WithRSAEncryption, NS_HASH_SHA512 },
572
#endif
573
#ifdef NID_ecdsa_with_SHA512
574
    { NID_ecdsa_with_SHA512, NS_HASH_SHA512 },
575
#endif
576
};
577
578
int
579
_nid2ht(int nid)
580
0
{
581
0
    int i;
582
583
0
    for (i = 0; i < sizeof(_htmap) / sizeof(_htmap[0]); i++) {
584
0
        if (_htmap[i].nid == nid)
585
0
            return _htmap[i].ht;
586
0
    }
587
0
    return 0;
588
0
}
589
590
#ifndef NETSNMP_FEATURE_REMOVE_OPENSSL_HT2NID
591
int
592
_ht2nid(int ht)
593
0
{
594
0
    int i;
595
596
0
    for (i = 0; i < sizeof(_htmap) / sizeof(_htmap[0]); i++) {
597
0
        if (_htmap[i].ht == ht)
598
0
            return _htmap[i].nid;
599
0
    }
600
0
    return 0;
601
0
}
602
#endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_HT2NID */
603
604
/**
605
 * returns allocated pointer caller must free.
606
 */
607
int
608
netsnmp_openssl_cert_get_hash_type(X509 *ocert)
609
0
{
610
0
    if (NULL == ocert)
611
0
        return 0;
612
613
0
    return _nid2ht(X509_get_signature_nid(ocert));
614
0
}
615
616
/**
617
 * returns allocated pointer caller must free.
618
 */
619
char *
620
netsnmp_openssl_cert_get_fingerprint(X509 *ocert, int alg)
621
0
{
622
0
    u_char           fingerprint[EVP_MAX_MD_SIZE];
623
0
    u_int            fingerprint_len, nid;
624
0
    const EVP_MD    *digest;
625
0
    char            *result = NULL;
626
627
0
    if (NULL == ocert)
628
0
        return NULL;
629
630
0
    nid = X509_get_signature_nid(ocert);
631
0
    DEBUGMSGT(("9:openssl:fingerprint", "alg %d, cert nid %d (%d)\n", alg, nid,
632
0
               _nid2ht(nid)));
633
        
634
0
    if ((-1 == alg) && nid)
635
0
        alg = _nid2ht(nid);
636
637
0
    switch (alg) {
638
0
        case NS_HASH_MD5:
639
0
            snmp_log(LOG_ERR, "hash type md5 not yet supported\n");
640
0
            return NULL;
641
0
            break;
642
        
643
0
        case NS_HASH_NONE:
644
0
            snmp_log(LOG_ERR, "hash type none not supported. using SHA1\n");
645
0
            NETSNMP_FALLTHROUGH;
646
647
0
        case NS_HASH_SHA1:
648
0
            digest = EVP_sha1();
649
0
            break;
650
651
0
#ifdef HAVE_EVP_SHA224
652
0
        case NS_HASH_SHA224:
653
0
            digest = EVP_sha224();
654
0
            break;
655
656
0
        case NS_HASH_SHA256:
657
0
            digest = EVP_sha256();
658
0
            break;
659
660
0
#endif
661
0
#ifdef HAVE_EVP_SHA384
662
0
        case NS_HASH_SHA384:
663
0
            digest = EVP_sha384();
664
0
            break;
665
666
0
        case NS_HASH_SHA512:
667
0
            digest = EVP_sha512();
668
0
            break;
669
0
#endif
670
671
0
        default:
672
0
            snmp_log(LOG_ERR, "unknown hash algorithm %d\n", alg);
673
0
            return NULL;
674
0
    }
675
676
0
    if (_nid2ht(nid) != alg) {
677
0
        DEBUGMSGT(("openssl:fingerprint",
678
0
                   "WARNING: alg %d does not match cert alg %d\n",
679
0
                   alg, _nid2ht(nid)));
680
0
    }
681
0
    if (X509_digest(ocert,digest,fingerprint,&fingerprint_len)) {
682
0
        binary_to_hex(fingerprint, fingerprint_len, &result);
683
0
        if (NULL == result)
684
0
            snmp_log(LOG_ERR, "failed to hexify fingerprint\n");
685
0
        else
686
0
            DEBUGMSGT(("9:openssl:fingerprint", "fingerprint %s\n", result));
687
0
    }
688
0
    else
689
0
        snmp_log(LOG_ERR,"failed to compute fingerprint\n");
690
691
0
    return result;
692
0
}
693
694
/**
695
 * get container of netsnmp_cert_map structures from an ssl connection
696
 * certificate chain.
697
 */
698
netsnmp_container *
699
netsnmp_openssl_get_cert_chain(SSL *ssl)
700
0
{
701
0
    X509                  *ocert, *ocert_tmp;
702
0
    STACK_OF(X509)        *ochain;
703
0
    char                  *fingerprint;
704
0
    netsnmp_container     *chain_map;
705
0
    netsnmp_cert_map      *cert_map;
706
0
    int                    i, sk_num_res, rc;
707
708
0
    netsnmp_assert_or_return(ssl != NULL, NULL);
709
710
0
    ocert = SSL_get_peer_certificate(ssl);
711
0
    if (!ocert) {
712
        /** no peer cert */
713
0
        snmp_log(LOG_ERR, "SSL peer has no certificate\n");
714
0
        return NULL;
715
0
    }
716
0
    DEBUGIF("9:cert:dump") {
717
0
        netsnmp_openssl_cert_dump_extensions(ocert);
718
0
    }
719
720
    /*
721
     * get fingerprint and save it
722
     */
723
0
    fingerprint = netsnmp_openssl_cert_get_fingerprint(ocert, NS_HASH_SHA1);
724
0
    if (NULL == fingerprint) {
725
0
        X509_free(ocert);
726
0
        return NULL;
727
0
    }
728
729
    /*
730
     * allocate cert map. Don't pass in fingerprint, since it would strdup
731
     * it and we've already got a copy.
732
     */
733
0
    cert_map = netsnmp_cert_map_alloc(NULL, ocert);
734
0
    if (NULL == cert_map) {
735
0
        free(fingerprint);
736
0
        X509_free(ocert);
737
0
        return NULL;
738
0
    }
739
0
    cert_map->fingerprint = fingerprint;
740
0
    cert_map->hashType = netsnmp_openssl_cert_get_hash_type(ocert);
741
742
0
    chain_map = netsnmp_cert_map_container_create(0); /* no fp subcontainer */
743
0
    if (NULL == chain_map) {
744
0
        netsnmp_cert_map_free(cert_map);
745
0
        X509_free(ocert);
746
0
        return NULL;
747
0
    }
748
0
    CONTAINER_SET_OPTIONS(chain_map, CONTAINER_KEY_UNSORTED, rc);
749
    
750
0
    CONTAINER_INSERT(chain_map, cert_map);
751
752
    /** check for a chain to a CA */
753
0
    ochain = SSL_get_peer_cert_chain(ssl);
754
0
    sk_num_res = sk_X509_num(ochain);
755
0
    if (!ochain || sk_num_res == 0) {
756
0
        DEBUGMSGT(("ssl:cert:chain", "peer has no cert chain\n"));
757
0
    }
758
0
    else {
759
        /*
760
         * loop over chain, adding fingerprint / cert for each
761
         */
762
0
        DEBUGMSGT(("ssl:cert:chain", "examining cert chain\n"));
763
0
        for(i = 0; i < sk_num_res; ++i) {
764
0
            ocert_tmp = sk_X509_value(ochain, i);
765
0
            if (ocert_tmp == ocert || X509_cmp(ocert_tmp, ocert) == 0)
766
0
                continue;
767
0
            fingerprint = netsnmp_openssl_cert_get_fingerprint(ocert_tmp, NS_HASH_SHA1);
768
0
            if (NULL == fingerprint)
769
0
                break;
770
0
            cert_map = netsnmp_cert_map_alloc(NULL, ocert_tmp);
771
0
            if (NULL == cert_map) {
772
0
                free(fingerprint);
773
0
                break;
774
0
            }
775
0
            cert_map->fingerprint = fingerprint;
776
0
            cert_map->hashType = netsnmp_openssl_cert_get_hash_type(ocert_tmp);
777
778
0
            CONTAINER_INSERT(chain_map, cert_map);
779
0
        } /* chain loop */
780
        /*
781
         * if we broke out of loop before finishing, clean up
782
         */
783
0
        if (i < sk_num_res)
784
0
            CONTAINER_FREE_ALL(chain_map, NULL);
785
0
    } /* got peer chain */
786
787
0
    DEBUGMSGT(("ssl:cert:chain", "found %" NETSNMP_PRIz "u certs in chain\n",
788
0
               CONTAINER_SIZE(chain_map)));
789
0
    if (CONTAINER_SIZE(chain_map) == 0) {
790
0
        CONTAINER_FREE(chain_map);
791
0
        chain_map = NULL;
792
0
    }
793
794
0
    X509_free(ocert);
795
0
    return chain_map;
796
0
}
797
798
/*
799
tlstmCertSANRFC822Name "Maps a subjectAltName's rfc822Name to a
800
                  tmSecurityName.  The local part of the rfc822Name is
801
                  passed unaltered but the host-part of the name must
802
                  be passed in lower case.
803
                  Example rfc822Name Field:  FooBar@Example.COM
804
                  is mapped to tmSecurityName: FooBar@example.com"
805
806
tlstmCertSANDNSName "Maps a subjectAltName's dNSName to a
807
                  tmSecurityName after first converting it to all
808
                  lower case."
809
810
tlstmCertSANIpAddress "Maps a subjectAltName's iPAddress to a
811
                  tmSecurityName by transforming the binary encoded
812
                  address as follows:
813
                  1) for IPv4 the value is converted into a decimal
814
                     dotted quad address (e.g. '192.0.2.1')
815
                  2) for IPv6 addresses the value is converted into a
816
                     32-character all lowercase hexadecimal string
817
                     without any colon separators.
818
819
                     Note that the resulting length is the maximum
820
                     length supported by the View-Based Access Control
821
                     Model (VACM).  Note that using both the Transport
822
                     Security Model's support for transport prefixes
823
                     (see the SNMP-TSM-MIB's
824
                     snmpTsmConfigurationUsePrefix object for details)
825
                     will result in securityName lengths that exceed
826
                     what VACM can handle."
827
828
tlstmCertSANAny "Maps any of the following fields using the
829
                  corresponding mapping algorithms:
830
                  | rfc822Name | tlstmCertSANRFC822Name |
831
                  | dNSName    | tlstmCertSANDNSName    |
832
                  | iPAddress  | tlstmCertSANIpAddress  |
833
                  The first matching subjectAltName value found in the
834
                  certificate of the above types MUST be used when
835
                  deriving the tmSecurityName."
836
*/
837
char *
838
_cert_get_san_type(X509 *ocert, int mapType)
839
0
{
840
0
    GENERAL_NAMES      *onames;
841
0
    const GENERAL_NAME *oname = NULL;
842
0
    char               *buf = NULL, *lower = NULL;
843
0
    int                 count, i;
844
 
845
0
    onames = (GENERAL_NAMES *)X509_get_ext_d2i(ocert, NID_subject_alt_name,
846
0
                                               NULL, NULL );
847
0
    if (NULL == onames)
848
0
        return NULL;
849
850
0
    count = sk_GENERAL_NAME_num(onames);
851
852
0
    for (i=0 ; i <count; ++i)  {
853
0
        oname = sk_GENERAL_NAME_value(onames, i);
854
855
0
        if (GEN_DNS == oname->type) {
856
0
            if ((TSNM_tlstmCertSANDNSName == mapType) ||
857
0
                (TSNM_tlstmCertSANAny == mapType)) {
858
0
                lower = buf = _extract_oname( oname );
859
0
                break;
860
0
            }
861
0
        }
862
0
        else if (GEN_IPADD == oname->type) {
863
0
            if ((TSNM_tlstmCertSANIpAddress == mapType) ||
864
0
                (TSNM_tlstmCertSANAny == mapType)) {
865
0
                buf = _extract_oname(oname);
866
0
                break;
867
0
            }
868
0
        }
869
0
        else if (GEN_EMAIL == oname->type) {
870
0
            if ((TSNM_tlstmCertSANRFC822Name == mapType) ||
871
0
                (TSNM_tlstmCertSANAny == mapType)) {
872
0
                buf = _extract_oname(oname);
873
0
                lower = strchr(buf, '@');
874
0
                if (NULL == lower) {
875
0
                    DEBUGMSGT(("openssl:secname:extract",
876
0
                               "email %s has no '@'!\n", buf));
877
0
                }
878
0
                else {
879
0
                    ++lower;
880
0
                    break;
881
0
                }
882
0
            }
883
            
884
0
        }
885
0
    } /* for loop */
886
887
0
    if (lower)
888
0
        for ( ; *lower; ++lower )
889
0
            *lower = tolower(0xFF & *lower);
890
0
    DEBUGMSGT(("openssl:cert:extension:san", "#%d type %d: %s\n", i,
891
0
               oname ? oname->type : -1, buf ? buf : "NULL"));
892
893
0
    GENERAL_NAMES_free(onames);
894
0
    return buf;
895
0
}
896
897
int
898
netsnmp_openssl_cert_check_host(X509 *ocert, const char *hostname)
899
0
{
900
0
    GENERAL_NAMES      *onames;
901
0
    const GENERAL_NAME *oname = NULL;
902
0
    char               *buf = NULL;
903
0
    int                 count, i, match = 0;
904
0
    const char         *dot;
905
906
0
    if (!ocert || !hostname)
907
0
        return 0;
908
909
    /* We only support wildcard patterns like *.example.com */
910
0
    if (hostname[0] != '*' || hostname[1] != '.')
911
0
        return 0;
912
913
0
    onames = (GENERAL_NAMES *)X509_get_ext_d2i(ocert, NID_subject_alt_name,
914
0
                                               NULL, NULL);
915
0
    if (onames) {
916
0
        count = sk_GENERAL_NAME_num(onames);
917
0
        for (i = 0; i < count; ++i) {
918
0
            oname = sk_GENERAL_NAME_value(onames, i);
919
0
            if (GEN_DNS == oname->type) {
920
0
                buf = _extract_oname(oname);
921
0
                if (buf) {
922
0
                    dot = strchr(buf, '.');
923
0
                    if (dot && strcasecmp(dot, hostname + 1) == 0) {
924
0
                        match = 1;
925
0
                    }
926
0
                    free(buf);
927
0
                    if (match)
928
0
                        break;
929
0
                }
930
0
            }
931
0
        }
932
0
        GENERAL_NAMES_free(onames);
933
0
    }
934
935
0
    if (!match) {
936
0
        buf = netsnmp_openssl_cert_get_commonName(ocert, NULL, NULL);
937
0
        if (buf) {
938
0
            dot = strchr(buf, '.');
939
0
            if (dot && strcasecmp(dot, hostname + 1) == 0) {
940
0
                match = 1;
941
0
            }
942
0
            free(buf);
943
0
        }
944
0
    }
945
946
0
    return match;
947
0
}
948
949
char *
950
netsnmp_openssl_extract_secname(netsnmp_cert_map *cert_map,
951
                                netsnmp_cert_map *peer_cert)
952
0
{
953
0
    char       *rtn = NULL;
954
955
0
    if (NULL == cert_map)
956
0
        return NULL;
957
958
0
    DEBUGMSGT(("openssl:secname:extract",
959
0
               "checking priority %d, san of type %d for %s\n",
960
0
               cert_map->priority, cert_map->mapType, peer_cert->fingerprint));
961
962
0
    switch(cert_map->mapType) {
963
0
        case TSNM_tlstmCertSpecified:
964
0
            rtn = strdup(cert_map->data);
965
0
            break;
966
967
0
        case TSNM_tlstmCertSANRFC822Name:
968
0
        case TSNM_tlstmCertSANDNSName:
969
0
        case TSNM_tlstmCertSANIpAddress:
970
0
        case TSNM_tlstmCertSANAny:
971
0
            if (NULL == peer_cert) {
972
0
                DEBUGMSGT(("openssl:secname:extract", "no peer cert for %s\n",
973
0
                           cert_map->fingerprint));
974
0
                break;
975
0
            }
976
0
            rtn = _cert_get_san_type(peer_cert->ocert, cert_map->mapType);
977
0
            if (NULL == rtn) {
978
0
                DEBUGMSGT(("openssl:secname:extract", "no san for %s\n",
979
0
                           peer_cert->fingerprint));
980
0
            }
981
0
            break;
982
983
0
        case TSNM_tlstmCertCommonName:
984
0
            if (NULL == peer_cert) {
985
0
                DEBUGMSGT(("openssl:secname:extract", "no peer cert for %s\n",
986
0
                           cert_map->fingerprint));
987
0
                break;
988
0
            }
989
0
            rtn = netsnmp_openssl_cert_get_commonName(peer_cert->ocert, NULL,
990
0
                                                       NULL);
991
0
            break;
992
0
        default:
993
0
            snmp_log(LOG_ERR, "cant extract secname for unknown map type %d\n",
994
0
                     cert_map->mapType);
995
0
            break;
996
0
    } /* switch mapType */
997
998
0
    if (rtn) {
999
0
        DEBUGMSGT(("openssl:secname:extract",
1000
0
                   "found map %d, type %d for %s: %s\n", cert_map->priority,
1001
0
                   cert_map->mapType, peer_cert->fingerprint, rtn));
1002
0
        if (strlen(rtn) >32) {
1003
0
            DEBUGMSGT(("openssl:secname:extract",
1004
0
                       "secName longer than 32 chars! dropping...\n"));
1005
0
            SNMP_FREE(rtn);
1006
0
        }
1007
0
    }
1008
0
    else
1009
0
        DEBUGMSGT(("openssl:secname:extract",
1010
0
                   "no map of type %d for %s\n",
1011
0
                   cert_map->mapType, peer_cert->fingerprint));
1012
0
    return rtn;
1013
0
}
1014
1015
int
1016
netsnmp_openssl_cert_issued_by(X509 *issuer, X509 *cert)
1017
0
{
1018
0
    return (X509_check_issued(issuer, cert) == X509_V_OK);
1019
0
}
1020
1021
1022
void
1023
netsnmp_openssl_null_checks(SSL *ssl, int *null_auth, int *null_cipher)
1024
0
{
1025
0
    const SSL_CIPHER *cipher;
1026
0
    char           tmp_buf[128], *cipher_alg, *auth_alg;
1027
1028
0
    if (null_auth)
1029
0
        *null_auth = -1; /* unknown */
1030
0
    if (null_cipher)
1031
0
        *null_cipher = -1; /* unknown */
1032
0
    if (NULL == ssl)
1033
0
        return;
1034
1035
0
    cipher = SSL_get_current_cipher(ssl);
1036
0
    if (NULL == cipher) {
1037
0
        DEBUGMSGTL(("ssl:cipher", "no cipher yet\n"));
1038
0
        return;
1039
0
    }
1040
0
    SSL_CIPHER_description(NETSNMP_REMOVE_CONST(SSL_CIPHER *, cipher), tmp_buf, sizeof(tmp_buf));
1041
    /** no \n since tmp_buf already has one */
1042
0
    DEBUGMSGTL(("ssl:cipher", "current cipher: %s", tmp_buf));
1043
1044
    /*
1045
     * run "openssl ciphers -v eNULL" and "openssl ciphers -v aNULL"
1046
     * to see NULL encryption/authentication algorithms. e.g.
1047
     *
1048
     * EXP-ADH-RC4-MD5 SSLv3 Kx=DH(512) Au=None Enc=RC4(40) Mac=MD5  export
1049
     * NULL-SHA        SSLv3 Kx=RSA     Au=RSA  Enc=None    Mac=SHA1
1050
     */
1051
0
    if (null_cipher) {
1052
0
        cipher_alg = strstr(tmp_buf, "Enc=");
1053
0
        if (cipher_alg) {
1054
0
            cipher_alg += 4;
1055
0
            if (strncmp(cipher_alg,"None", 4) == 0)
1056
0
                *null_cipher = 1;
1057
0
            else
1058
0
                *null_cipher = 0;
1059
0
        }
1060
0
    }
1061
0
    if (null_auth) {
1062
0
        auth_alg = strstr(tmp_buf, "Au=");
1063
0
        if (auth_alg) {
1064
0
            auth_alg += 3;
1065
0
            if (strncmp(auth_alg,"None", 4) == 0)
1066
0
                *null_auth = 1;
1067
0
            else
1068
0
                *null_auth = 0;
1069
0
        }
1070
0
    }
1071
0
}
1072
1073
#ifndef HAVE_X509_GET_SIGNATURE_NID
1074
int X509_get_signature_nid(const X509 *x)
1075
{
1076
    return OBJ_obj2nid(x->sig_alg->algorithm);
1077
}
1078
#endif
1079
1080
#ifndef HAVE_ASN1_STRING_GET0_DATA
1081
const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x)
1082
{
1083
    return x->data;
1084
}
1085
#endif
1086
1087
#ifndef HAVE_X509_NAME_ENTRY_GET_OBJECT
1088
ASN1_OBJECT *X509_NAME_ENTRY_get_object(const X509_NAME_ENTRY *ne)
1089
{
1090
    if (ne == NULL)
1091
        return NULL;
1092
    return ne->object;
1093
}
1094
#endif
1095
1096
#ifndef HAVE_X509_NAME_ENTRY_GET_DATA
1097
ASN1_STRING *X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne)
1098
{
1099
    if (ne == NULL)
1100
        return NULL;
1101
    return ne->value;
1102
}
1103
#endif
1104
1105
#ifndef HAVE_TLS_METHOD
1106
const SSL_METHOD *TLS_method(void)
1107
{
1108
    return TLSv1_method();
1109
}
1110
#endif
1111
1112
#ifndef HAVE_DTLS_METHOD
1113
const SSL_METHOD *DTLS_method(void)
1114
{
1115
    return DTLSv1_method();
1116
}
1117
#endif
1118
1119
#endif /* NETSNMP_USE_OPENSSL && HAVE_LIBSSL && !defined(NETSNMP_FEATURE_REMOVE_CERT_UTIL) */