Coverage Report

Created: 2026-09-01 06:12

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