Bug Summary

File:src/usr.sbin/snmpd/usm.c
Warning:line 449, column 7
Although the value stored to 'a' is used in the enclosing expression, the value is never actually read from 'a'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name usm.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/snmpd/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.sbin/snmpd -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/snmpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/snmpd/usm.c
1/* $OpenBSD: usm.c,v 1.30 2023/12/22 13:03:16 martijn Exp $ */
2
3/*
4 * Copyright (c) 2012 GeNUA mbH
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/queue.h>
20#include <sys/types.h>
21
22#include <openssl/evp.h>
23#include <openssl/hmac.h>
24
25#ifdef DEBUG
26#include <assert.h>
27#endif
28#include <ber.h>
29#include <endian.h>
30#include <stdlib.h>
31#include <string.h>
32#include <strings.h>
33
34#include "application.h"
35#include "log.h"
36#include "mib.h"
37#include "snmp.h"
38#include "snmpd.h"
39
40SLIST_HEAD(, usmuser)struct { struct usmuser *slh_first; } usmuserlist;
41
42const EVP_MD *usm_get_md(enum usmauth);
43size_t usm_get_digestlen(enum usmauth);
44const EVP_CIPHER *usm_get_cipher(enum usmpriv);
45int usm_valid_digestlen(size_t digestlen);
46void usm_cb_digest(void *, size_t);
47int usm_valid_digest(struct snmp_message *, off_t, char *,
48 size_t);
49struct ber_element *usm_decrypt(struct snmp_message *,
50 struct ber_element *);
51ssize_t usm_crypt(struct snmp_message *, u_char *, int,
52 u_char *, int);
53char *usm_passwd2key(const EVP_MD *, char *, int *);
54
55void
56usm_generate_keys(void)
57{
58 struct usmuser *up;
59 const EVP_MD *md;
60 char *key;
61 int len;
62
63 SLIST_FOREACH(up, &usmuserlist, uu_next)for((up) = ((&usmuserlist)->slh_first); (up) != ((void
*)0); (up) = ((up)->uu_next.sle_next))
{
64 if ((md = usm_get_md(up->uu_auth)) == NULL((void *)0))
65 continue;
66
67 /* convert auth password to key */
68 len = 0;
69 key = usm_passwd2key(md, up->uu_authkey, &len);
70 free(up->uu_authkey);
71 up->uu_authkey = key;
72 up->uu_authkeylen = len;
73
74 /* optionally convert privacy password to key */
75 if (up->uu_priv != PRIV_NONE) {
76 arc4random_buf(&up->uu_salt, sizeof(up->uu_salt));
77
78 len = SNMP_CIPHER_KEYLEN16;
79 key = usm_passwd2key(md, up->uu_privkey, &len);
80 free(up->uu_privkey);
81 up->uu_privkey = key;
82 }
83 }
84 return;
85}
86
87const EVP_MD *
88usm_get_md(enum usmauth ua)
89{
90 switch (ua) {
91 case AUTH_MD5:
92 return EVP_md5();
93 case AUTH_SHA1:
94 return EVP_sha1();
95 case AUTH_SHA224:
96 return EVP_sha224();
97 case AUTH_SHA256:
98 return EVP_sha256();
99 case AUTH_SHA384:
100 return EVP_sha384();
101 case AUTH_SHA512:
102 return EVP_sha512();
103 case AUTH_NONE:
104 default:
105 return NULL((void *)0);
106 }
107}
108
109size_t
110usm_get_digestlen(enum usmauth ua)
111{
112 switch (ua) {
113 case AUTH_MD5:
114 case AUTH_SHA1:
115 return 12;
116 case AUTH_SHA224:
117 return 16;
118 case AUTH_SHA256:
119 return 24;
120 case AUTH_SHA384:
121 return 32;
122 case AUTH_SHA512:
123 return 48;
124 case AUTH_NONE:
125 default:
126 return 0;
127 }
128}
129
130const EVP_CIPHER *
131usm_get_cipher(enum usmpriv up)
132{
133 switch (up) {
134 case PRIV_DES:
135 return EVP_des_cbc();
136 case PRIV_AES:
137 return EVP_aes_128_cfb128();
138 case PRIV_NONE:
139 default:
140 return NULL((void *)0);
141 }
142}
143
144int
145usm_valid_digestlen(size_t digestlen)
146{
147 switch (digestlen) {
148 case 0:
149 case 12:
150 case 16:
151 case 24:
152 case 32:
153 case 48:
154 return 1;
155 default:
156 return 0;
157 }
158}
159
160struct usmuser *
161usm_newuser(char *name, const char **errp)
162{
163 struct usmuser *up = usm_finduser(name);
164 if (up != NULL((void *)0)) {
165 *errp = "user redefined";
166 return NULL((void *)0);
167 }
168 if ((up = calloc(1, sizeof(*up))) == NULL((void *)0))
169 fatal("usm");
170 up->uu_name = name;
171 SLIST_INSERT_HEAD(&usmuserlist, up, uu_next)do { (up)->uu_next.sle_next = (&usmuserlist)->slh_first
; (&usmuserlist)->slh_first = (up); } while (0)
;
172 return up;
173}
174
175const struct usmuser *
176usm_check_mincred(int minlevel, const char **errstr)
177{
178 struct usmuser *up;
179
180 if (minlevel == 0)
181 return NULL((void *)0);
182
183 SLIST_FOREACH(up, &usmuserlist, uu_next)for((up) = ((&usmuserlist)->slh_first); (up) != ((void
*)0); (up) = ((up)->uu_next.sle_next))
{
184 if (minlevel & SNMP_MSGFLAG_PRIV0x02 && up->uu_privkey == NULL((void *)0)) {
185 *errstr = "missing enckey";
186 return up;
187 }
188 if (minlevel & SNMP_MSGFLAG_AUTH0x01 && up->uu_authkey == NULL((void *)0)) {
189 *errstr = "missing authkey";
190 return up;
191 }
192 }
193 return NULL((void *)0);
194}
195
196struct usmuser *
197usm_finduser(char *name)
198{
199 struct usmuser *up;
200
201 SLIST_FOREACH(up, &usmuserlist, uu_next)for((up) = ((&usmuserlist)->slh_first); (up) != ((void
*)0); (up) = ((up)->uu_next.sle_next))
{
202 if (!strcmp(up->uu_name, name))
203 return up;
204 }
205 return NULL((void *)0);
206}
207
208int
209usm_checkuser(struct usmuser *up, const char **errp)
210{
211 if (up->uu_auth != AUTH_NONE && up->uu_authkey == NULL((void *)0)) {
212 *errp = "missing auth passphrase";
213 goto fail;
214 } else if (up->uu_auth == AUTH_NONE && up->uu_authkey != NULL((void *)0))
215 up->uu_auth = AUTH_DEFAULTAUTH_SHA1;
216
217 if (up->uu_priv != PRIV_NONE && up->uu_privkey == NULL((void *)0)) {
218 *errp = "missing priv passphrase";
219 goto fail;
220 } else if (up->uu_priv == PRIV_NONE && up->uu_privkey != NULL((void *)0))
221 up->uu_priv = PRIV_DEFAULTPRIV_AES;
222
223 if (up->uu_auth == AUTH_NONE && up->uu_priv != PRIV_NONE) {
224 /* Standard prohibits noAuthPriv */
225 *errp = "auth is mandatory with enc";
226 goto fail;
227 }
228
229 switch (up->uu_auth) {
230 case AUTH_NONE:
231 break;
232 case AUTH_MD5:
233 case AUTH_SHA1:
234 case AUTH_SHA224:
235 case AUTH_SHA256:
236 case AUTH_SHA384:
237 case AUTH_SHA512:
238 up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01;
239 break;
240 }
241
242 switch (up->uu_priv) {
243 case PRIV_NONE:
244 break;
245 case PRIV_DES:
246 case PRIV_AES:
247 up->uu_seclevel |= SNMP_MSGFLAG_PRIV0x02;
248 break;
249 }
250
251 return 0;
252
253fail:
254 free(up->uu_name);
255 free(up->uu_authkey);
256 free(up->uu_privkey);
257 SLIST_REMOVE(&usmuserlist, up, usmuser, uu_next)do { if ((&usmuserlist)->slh_first == (up)) { do { ((&
usmuserlist))->slh_first = ((&usmuserlist))->slh_first
->uu_next.sle_next; } while (0); } else { struct usmuser *
curelm = (&usmuserlist)->slh_first; while (curelm->
uu_next.sle_next != (up)) curelm = curelm->uu_next.sle_next
; curelm->uu_next.sle_next = curelm->uu_next.sle_next->
uu_next.sle_next; } ; } while (0)
;
258 free(up);
259 return -1;
260}
261
262struct ber_element *
263usm_decode(struct snmp_message *msg, struct ber_element *elm, const char **errp)
264{
265 struct snmp_stats *stats = &snmpd_env->sc_stats;
266 off_t offs, offs2;
267 char *usmparams;
268 size_t len;
269 size_t enginelen, userlen, digestlen, saltlen;
270 struct ber ber;
271 struct ber_element *usm = NULL((void *)0), *next = NULL((void *)0), *decr;
272 char *engineid;
273 char *user;
274 char *digest, *salt;
275 u_long now;
276 long long engine_boots, engine_time;
277
278 bzero(&ber, sizeof(ber));
279 offs = ober_getpos(elm);
280
281 if (ober_get_nstring(elm, (void *)&usmparams, &len) < 0) {
282 *errp = "cannot decode security params";
283 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
284 goto done;
285 }
286
287 ober_set_readbuf(&ber, usmparams, len);
288 usm = ober_read_elements(&ber, NULL((void *)0));
289 if (usm == NULL((void *)0)) {
290 *errp = "cannot decode security params";
291 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
292 goto done;
293 }
294
295#ifdef DEBUG
296 fprintf(stderr(&__sF[2]), "decode USM parameters:\n");
297 smi_debug_elements(usm);
298#endif
299
300 if (ober_scanf_elements(usm, "{xiixpxx$", &engineid, &enginelen,
301 &engine_boots, &engine_time, &user, &userlen, &offs2,
302 &digest, &digestlen, &salt, &saltlen) != 0) {
303 *errp = "cannot decode USM params";
304 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
305 goto done;
306 }
307
308 log_debug("USM: engineid '%s', engine boots %lld, engine time %lld, "
309 "user '%s'", tohexstr(engineid, enginelen), engine_boots,
310 engine_time, user);
311
312 if (enginelen > SNMPD_MAXENGINEIDLEN32 ||
313 userlen > SNMPD_MAXUSERNAMELEN32 ||
314 !usm_valid_digestlen(digestlen) ||
315 (saltlen != (MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0) ? SNMP_USM_SALTLEN8 : 0))) {
316 *errp = "bad field length";
317 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
318 goto done;
319 }
320
321 if (enginelen != snmpd_env->sc_engineid_len ||
322 memcmp(engineid, snmpd_env->sc_engineid, enginelen) != 0) {
323 *errp = "unknown engine id";
324 msg->sm_usmerr = OIDVAL_usmErrEngineId4;
325 stats->snmp_usmnosuchengine++;
326 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
327 goto done;
328 }
329
330 msg->sm_engine_boots = (u_int32_t)engine_boots;
331 msg->sm_engine_time = (u_int32_t)engine_time;
332
333 memcpy(msg->sm_username, user, userlen);
334 msg->sm_username[userlen] = '\0';
335 msg->sm_user = usm_finduser(msg->sm_username);
336 if (msg->sm_user == NULL((void *)0)) {
337 *errp = "no such user";
338 msg->sm_usmerr = OIDVAL_usmErrUserName3;
339 stats->snmp_usmnosuchuser++;
340 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
341 goto done;
342 }
343 if (MSG_SECLEVEL(msg)((msg)->sm_flags & (0x01 | 0x02)) > msg->sm_user->uu_seclevel) {
344 *errp = "unsupported security model";
345 msg->sm_usmerr = OIDVAL_usmErrSecLevel1;
346 stats->snmp_usmbadseclevel++;
347 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
348 goto done;
349 }
350
351 /*
352 * offs is the offset of the USM string within the serialized msg
353 * and offs2 the offset of the digest within the USM string.
354 */
355 if (!usm_valid_digest(msg, offs + offs2, digest, digestlen)) {
356 *errp = "bad msg digest";
357 msg->sm_usmerr = OIDVAL_usmErrDigest5;
358 stats->snmp_usmwrongdigest++;
359 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
360 goto done;
361 }
362
363 if (MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0)) {
364 memcpy(msg->sm_salt, salt, saltlen);
365 if ((decr = usm_decrypt(msg, elm->be_next)) == NULL((void *)0)) {
366 *errp = "cannot decrypt msg";
367 msg->sm_usmerr = OIDVAL_usmErrDecrypt6;
368 stats->snmp_usmdecrypterr++;
369 msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04;
370 goto done;
371 }
372 ober_replace_elements(elm, decr);
373 }
374
375 if (MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0)) {
376 now = snmpd_engine_time();
377 if (engine_boots != snmpd_env->sc_engine_boots ||
378 engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW150) ||
379 engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW150)) {
380 *errp = "out of time window";
381 msg->sm_usmerr = OIDVAL_usmErrTimeWindow2;
382 stats->snmp_usmtimewindow++;
383 goto done;
384 }
385 }
386
387 next = elm->be_next;
388
389done:
390 ober_free(&ber);
391 if (usm != NULL((void *)0))
392 ober_free_elements(usm);
393 return next;
394}
395
396struct ber_element *
397usm_encode(struct snmp_message *msg, struct ber_element *e)
398{
399 struct ber ber;
400 struct ber_element *usm, *a, *res = NULL((void *)0);
401 void *ptr;
402 char digest[SNMP_USM_MAXDIGESTLEN48];
403 size_t digestlen, saltlen;
404 ssize_t len;
405
406 msg->sm_digest_offs = 0;
407 bzero(&ber, sizeof(ber));
408
409 usm = ober_add_sequence(NULL((void *)0));
410
411 if (MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0)) {
412 /*
413 * Fill in enough zeroes and remember the position within the
414 * messages. The digest will be calculated once the message
415 * is complete.
416 */
417#ifdef DEBUG
418 assert(msg->sm_user != NULL((void *)0));
419#endif
420 bzero(digest, sizeof(digest));
421 digestlen = usm_get_digestlen(msg->sm_user->uu_auth);
422 } else
423 digestlen = 0;
424
425 if (MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0)) {
426#ifdef DEBUG
427 assert(msg->sm_user != NULL((void *)0));
428#endif
429 ++(msg->sm_user->uu_salt);
430 memcpy(msg->sm_salt, &msg->sm_user->uu_salt,
431 sizeof(msg->sm_salt));
432 saltlen = sizeof(msg->sm_salt);
433 } else
434 saltlen = 0;
435
436 msg->sm_engine_boots = (u_int32_t)snmpd_env->sc_engine_boots;
437 msg->sm_engine_time = (u_int32_t)snmpd_engine_time();
438 if ((a = ober_printf_elements(usm, "xdds",
439 snmpd_env->sc_engineid, snmpd_env->sc_engineid_len,
440 msg->sm_engine_boots, msg->sm_engine_time,
441 msg->sm_username)) == NULL((void *)0))
442 goto done;
443
444 if ((a = ober_add_nstring(a, digest, digestlen)) == NULL((void *)0))
445 goto done;
446 if (digestlen > 0)
447 ober_set_writecallback(a, usm_cb_digest, msg);
448
449 if ((a = ober_add_nstring(a, msg->sm_salt, saltlen)) == NULL((void *)0))
Although the value stored to 'a' is used in the enclosing expression, the value is never actually read from 'a'
450 goto done;
451
452#ifdef DEBUG
453 fprintf(stderr(&__sF[2]), "encode USM parameters:\n");
454 smi_debug_elements(usm);
455#endif
456 len = ober_write_elements(&ber, usm);
457 if (ober_get_writebuf(&ber, &ptr) > 0) {
458 res = ober_add_nstring(e, (char *)ptr, len);
459 if (digestlen > 0)
460 ober_set_writecallback(res, usm_cb_digest, msg);
461 }
462
463done:
464 ober_free(&ber);
465 ober_free_elements(usm);
466 return res;
467}
468
469void
470usm_cb_digest(void *arg, size_t offs)
471{
472 struct snmp_message *msg = arg;
473 msg->sm_digest_offs += offs;
474}
475
476struct ber_element *
477usm_encrypt(struct snmp_message *msg, struct ber_element *pdu)
478{
479 struct ber ber;
480 struct ber_element *encrpdu = NULL((void *)0);
481 void *ptr;
482 ssize_t elen, len;
483 u_char *encbuf;
484
485 if (!MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0))
486 return pdu;
487
488 bzero(&ber, sizeof(ber));
489
490#ifdef DEBUG
491 fprintf(stderr(&__sF[2]), "encrypted PDU:\n");
492 smi_debug_elements(pdu);
493#endif
494
495 len = ober_write_elements(&ber, pdu);
496 if (ober_get_writebuf(&ber, &ptr) > 0 &&
497 (encbuf = malloc(len + EVP_MAX_BLOCK_LENGTH32)) != NULL((void *)0)) {
498 elen = usm_crypt(msg, ptr, len, encbuf, 1);
499 if (elen > 0)
500 encrpdu = ober_add_nstring(NULL((void *)0), (char *)encbuf, elen);
501 free(encbuf);
502 }
503
504 ober_free(&ber);
505 ober_free_elements(pdu);
506 return encrpdu;
507}
508
509/*
510 * Calculate message digest and replace within message
511 */
512void
513usm_finalize_digest(struct snmp_message *msg, char *buf, ssize_t len)
514{
515 const EVP_MD *md;
516 u_char digest[EVP_MAX_MD_SIZE64];
517 size_t digestlen;
518 unsigned hlen;
519
520 if (msg->sm_resp == NULL((void *)0) ||
521 !MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0) ||
522 msg->sm_user == NULL((void *)0) ||
523 msg->sm_digest_offs == 0 ||
524 len <= 0)
525 return;
526
527 if ((digestlen = usm_get_digestlen(msg->sm_user->uu_auth)) == 0)
528 return;
529 bzero(digest, digestlen);
530#ifdef DEBUG
531 assert(msg->sm_digest_offs + digestlen <= (size_t)len);
532 assert(!memcmp(buf + msg->sm_digest_offs, digest, digestlen));
533#endif
534
535 if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL((void *)0))
536 return;
537
538 HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen,
539 (u_char*)buf, (size_t)len, digest, &hlen);
540
541 memcpy(buf + msg->sm_digest_offs, digest, digestlen);
542 return;
543}
544
545void
546usm_make_report(struct snmp_message *msg)
547{
548 appl_report(msg, 0, &OID(MIB_usmStats, msg->sm_usmerr, 0)(struct ber_oid){ { 1, 3, 6, 1, 6, 3, 15, 1, 1, msg->sm_usmerr
, 0 }, (sizeof((uint32_t []) { 1, 3, 6, 1, 6, 3, 15, 1, 1, msg
->sm_usmerr, 0 }) / sizeof(uint32_t)) }
);
549}
550
551int
552usm_valid_digest(struct snmp_message *msg, off_t offs,
553 char *digest, size_t digestlen)
554{
555 const EVP_MD *md;
556 u_char exp_digest[EVP_MAX_MD_SIZE64];
557 unsigned hlen;
558
559 if (!MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0))
560 return 1;
561
562 if (digestlen != usm_get_digestlen(msg->sm_user->uu_auth))
563 return 0;
564
565#ifdef DEBUG
566 assert(offs + digestlen <= msg->sm_datalen);
567 assert(bcmp(&msg->sm_data[offs], digest, digestlen) == 0);
568#endif
569
570 if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL((void *)0))
571 return 0;
572
573 memset(&msg->sm_data[offs], 0, digestlen);
574 HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen,
575 msg->sm_data, msg->sm_datalen, exp_digest, &hlen);
576 /* we don't bother to restore the original message */
577
578 if (hlen < digestlen)
579 return 0;
580
581 return memcmp(digest, exp_digest, digestlen) == 0;
582}
583
584struct ber_element *
585usm_decrypt(struct snmp_message *msg, struct ber_element *encr)
586{
587 u_char *privstr;
588 size_t privlen;
589 u_char *buf;
590 struct ber ber;
591 struct ber_element *scoped_pdu = NULL((void *)0);
592 ssize_t scoped_pdu_len;
593
594 if (ober_get_nstring(encr, (void *)&privstr, &privlen) < 0 ||
595 (buf = malloc(privlen)) == NULL((void *)0))
596 return NULL((void *)0);
597
598 scoped_pdu_len = usm_crypt(msg, privstr, (int)privlen, buf, 0);
599 if (scoped_pdu_len < 0) {
600 free(buf);
601 return NULL((void *)0);
602 }
603
604 bzero(&ber, sizeof(ber));
605 ober_set_application(&ber, smi_application);
606 ober_set_readbuf(&ber, buf, scoped_pdu_len);
607 scoped_pdu = ober_read_elements(&ber, NULL((void *)0));
608
609#ifdef DEBUG
610 if (scoped_pdu != NULL((void *)0)) {
611 fprintf(stderr(&__sF[2]), "decrypted scoped PDU:\n");
612 smi_debug_elements(scoped_pdu);
613 }
614#endif
615
616 ober_free(&ber);
617 free(buf);
618 return scoped_pdu;
619}
620
621ssize_t
622usm_crypt(struct snmp_message *msg, u_char *inbuf, int inlen, u_char *outbuf,
623 int do_encrypt)
624{
625 const EVP_CIPHER *cipher;
626 EVP_CIPHER_CTX *ctx;
627 u_char *privkey;
628 int i;
629 u_char iv[EVP_MAX_IV_LENGTH16];
630 int len, len2;
631 int rv;
632 u_int32_t ivv;
633
634 if ((cipher = usm_get_cipher(msg->sm_user->uu_priv)) == NULL((void *)0))
635 return -1;
636
637 privkey = (u_char *)msg->sm_user->uu_privkey;
638#ifdef DEBUG
639 assert(privkey != NULL((void *)0));
640#endif
641 switch (msg->sm_user->uu_priv) {
642 case PRIV_DES:
643 /* RFC3414, chap 8.1.1.1. */
644 for (i = 0; i < 8; i++)
645 iv[i] = msg->sm_salt[i] ^ privkey[SNMP_USM_SALTLEN8 + i];
646 break;
647 case PRIV_AES:
648 /* RFC3826, chap 3.1.2.1. */
649 ivv = htobe32(msg->sm_engine_boots)(__uint32_t)(__builtin_constant_p(msg->sm_engine_boots) ? (
__uint32_t)(((__uint32_t)(msg->sm_engine_boots) & 0xff
) << 24 | ((__uint32_t)(msg->sm_engine_boots) & 0xff00
) << 8 | ((__uint32_t)(msg->sm_engine_boots) & 0xff0000
) >> 8 | ((__uint32_t)(msg->sm_engine_boots) & 0xff000000
) >> 24) : __swap32md(msg->sm_engine_boots))
;
650 memcpy(iv, &ivv, sizeof(ivv));
651 ivv = htobe32(msg->sm_engine_time)(__uint32_t)(__builtin_constant_p(msg->sm_engine_time) ? (
__uint32_t)(((__uint32_t)(msg->sm_engine_time) & 0xff)
<< 24 | ((__uint32_t)(msg->sm_engine_time) & 0xff00
) << 8 | ((__uint32_t)(msg->sm_engine_time) & 0xff0000
) >> 8 | ((__uint32_t)(msg->sm_engine_time) & 0xff000000
) >> 24) : __swap32md(msg->sm_engine_time))
;
652 memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
653 memcpy(iv + 2 * sizeof(ivv), msg->sm_salt, SNMP_USM_SALTLEN8);
654 break;
655 default:
656 return -1;
657 }
658
659 if ((ctx = EVP_CIPHER_CTX_new()) == NULL((void *)0))
660 return -1;
661
662 if (!EVP_CipherInit(ctx, cipher, privkey, iv, do_encrypt)) {
663 EVP_CIPHER_CTX_free(ctx);
664 return -1;
665 }
666
667 if (!do_encrypt)
668 EVP_CIPHER_CTX_set_padding(ctx, 0);
669
670 if (EVP_CipherUpdate(ctx, outbuf, &len, inbuf, inlen) &&
671 EVP_CipherFinal_ex(ctx, outbuf + len, &len2))
672 rv = len + len2;
673 else
674 rv = -1;
675
676 EVP_CIPHER_CTX_free(ctx);
677 return rv;
678}
679
680/*
681 * RFC3414, Password to Key Algorithm
682 */
683char *
684usm_passwd2key(const EVP_MD *md, char *passwd, int *maxlen)
685{
686 EVP_MD_CTX *ctx;
687 int i, count;
688 u_char *pw, *c;
689 u_char pwbuf[2 * EVP_MAX_MD_SIZE64 + SNMPD_MAXENGINEIDLEN32];
690 u_char keybuf[EVP_MAX_MD_SIZE64];
691 unsigned dlen;
692 char *key;
693
694 if ((ctx = EVP_MD_CTX_new()) == NULL((void *)0))
695 return NULL((void *)0);
696 if (!EVP_DigestInit_ex(ctx, md, NULL((void *)0))) {
697 EVP_MD_CTX_free(ctx);
698 return NULL((void *)0);
699 }
700 pw = (u_char *)passwd;
701 for (count = 0; count < 1048576; count += 64) {
702 c = pwbuf;
703 for (i = 0; i < 64; i++) {
704 if (*pw == '\0')
705 pw = (u_char *)passwd;
706 *c++ = *pw++;
707 }
708 if (!EVP_DigestUpdate(ctx, pwbuf, 64)) {
709 EVP_MD_CTX_free(ctx);
710 return NULL((void *)0);
711 }
712 }
713 if (!EVP_DigestFinal_ex(ctx, keybuf, &dlen)) {
714 EVP_MD_CTX_free(ctx);
715 return NULL((void *)0);
716 }
717
718 /* Localize the key */
719#ifdef DEBUG
720 assert(snmpd_env->sc_engineid_len <= SNMPD_MAXENGINEIDLEN32);
721#endif
722 memcpy(pwbuf, keybuf, dlen);
723 memcpy(pwbuf + dlen, snmpd_env->sc_engineid,
724 snmpd_env->sc_engineid_len);
725 memcpy(pwbuf + dlen + snmpd_env->sc_engineid_len, keybuf, dlen);
726
727 if (!EVP_DigestInit_ex(ctx, md, NULL((void *)0)) ||
728 !EVP_DigestUpdate(ctx, pwbuf,
729 2 * dlen + snmpd_env->sc_engineid_len) ||
730 !EVP_DigestFinal_ex(ctx, keybuf, &dlen)) {
731 EVP_MD_CTX_free(ctx);
732 return NULL((void *)0);
733 }
734 EVP_MD_CTX_free(ctx);
735
736 if (*maxlen > 0 && dlen > (unsigned)*maxlen)
737 dlen = (unsigned)*maxlen;
738 if ((key = malloc(dlen)) == NULL((void *)0))
739 fatal("key");
740 memcpy(key, keybuf, dlen);
741 *maxlen = (int)dlen;
742 return key;
743}