Bug Summary

File:src/usr.bin/snmp/usm.c
Warning:line 516, column 3
Value stored to 'keylen' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name usm.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -fno-rounding-math -mconstructor-aliases -munwind-tables -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.bin/snmp/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/snmp -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/snmp/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -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/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/snmp/usm.c
1/* $OpenBSD: usm.c,v 1.7 2022/01/05 16:41:07 tb Exp $ */
2
3/*
4 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
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/time.h>
20
21#include <openssl/evp.h>
22#include <openssl/hmac.h>
23
24#include <ber.h>
25#include <errno(*__errno()).h>
26#include <string.h>
27#include <time.h>
28
29#include "smi.h"
30#include "snmp.h"
31#include "usm.h"
32
33#define USM_MAX_DIGESTLEN48 48
34#define USM_MAX_TIMEWINDOW150 150
35#define USM_SALTOFFSET8 8
36
37struct usm_sec {
38 struct snmp_sec snmp;
39 char *user;
40 size_t userlen;
41 int engineidset;
42 char *engineid;
43 size_t engineidlen;
44 enum usm_key_level authlevel;
45 const EVP_MD *digest;
46 char *authkey;
47 enum usm_key_level privlevel;
48 const EVP_CIPHER *cipher;
49 char *privkey;
50 int bootsset;
51 uint32_t boots;
52 int timeset;
53 uint32_t time;
54 struct timespec timecheck;
55};
56
57struct usm_cookie {
58 size_t digestoffset;
59 long long salt;
60 uint32_t boots;
61 uint32_t time;
62};
63
64static int usm_doinit(struct snmp_agent *);
65static char *usm_genparams(struct snmp_agent *, size_t *, void **);
66static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *);
67static struct ber_element *usm_encpdu(struct snmp_agent *agent,
68 struct ber_element *pdu, void *cookie);
69static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *,
70 char *, size_t, size_t *);
71static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
72 size_t, uint8_t, void **);
73struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *);
74static void usm_digest_pos(void *, size_t);
75static void usm_free(void *);
76static char *usm_passwd2mkey(const EVP_MD *, const char *);
77static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
78static size_t usm_digestlen(const EVP_MD *);
79
80struct snmp_sec *
81usm_init(const char *user, size_t userlen)
82{
83 struct snmp_sec *sec;
84 struct usm_sec *usm;
85
86 if (user == NULL((void *)0) || user[0] == '\0') {
87 errno(*__errno()) = EINVAL22;
88 return NULL((void *)0);
89 }
90
91 if ((sec = malloc(sizeof(*sec))) == NULL((void *)0))
92 return NULL((void *)0);
93
94 if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL((void *)0)) {
95 free(sec);
96 return NULL((void *)0);
97 }
98 if ((usm->user = malloc(userlen)) == NULL((void *)0)) {
99 free(sec);
100 free(usm);
101 return NULL((void *)0);
102 }
103 memcpy(usm->user, user, userlen);
104 usm->userlen = userlen;
105
106 sec->model = SNMP_SEC_USM;
107 sec->init = usm_doinit;
108 sec->genparams = usm_genparams;
109 sec->encpdu = usm_encpdu;
110 sec->parseparams = usm_parseparams;
111 sec->decpdu = usm_decpdu;
112 sec->finalparams = usm_finalparams;
113 sec->free = usm_free;
114 sec->freecookie = free;
115 sec->data = usm;
116 return sec;
117}
118
119static int
120usm_doinit(struct snmp_agent *agent)
121{
122 struct ber_element *ber;
123 struct usm_sec *usm = agent->v3->sec->data;
124 int level;
125 size_t userlen;
126
127 if (usm->engineidset && usm->bootsset && usm->timeset)
128 return 0;
129
130 level = agent->v3->level;
131 agent->v3->level = SNMP_MSGFLAG_REPORT0x04;
132 userlen = usm->userlen;
133 usm->userlen = 0;
134
135 if ((ber = snmp_get(agent, NULL((void *)0), 0)) == NULL((void *)0)) {
136 agent->v3->level = level;
137 usm->userlen = userlen;
138 return -1;
139 }
140 ober_free_element(ber);
141
142 agent->v3->level = level;
143 usm->userlen = userlen;
144
145 /*
146 * Ugly hack for HP Laserjet:
147 * This device returns the engineid on probing, but only returns boots
148 * and time after a packet has been sent with full auth/enc.
149 */
150 if (!usm->engineidset || !usm->bootsset || !usm->timeset) {
151 if ((ber = snmp_get(agent, NULL((void *)0), 0)) == NULL((void *)0))
152 return -1;
153 ober_free_element(ber);
154 }
155 return 0;
156}
157
158static char *
159usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
160{
161 struct ber ber;
162 struct ber_element *params, *digestelm;
163 struct usm_sec *usm = agent->v3->sec->data;
164 char digest[USM_MAX_DIGESTLEN48];
165 size_t digestlen = 0, saltlen = 0;
166 char *secparams = NULL((void *)0);
167 ssize_t berlen = 0;
168 struct usm_cookie *usmcookie;
169 struct timespec now, timediff;
170
171 bzero(digest, sizeof(digest));
172
173 if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL((void *)0))
174 return NULL((void *)0);
175 *cookie = usmcookie;
176
177 arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt));
178 if (usm->timeset) {
179 if (clock_gettime(CLOCK_MONOTONIC3, &now) == -1) {
180 free(usmcookie);
181 return NULL((void *)0);
182 }
183 timespecsub(&now, &(usm->timecheck), &timediff)do { (&timediff)->tv_sec = (&now)->tv_sec - (&
(usm->timecheck))->tv_sec; (&timediff)->tv_nsec =
(&now)->tv_nsec - (&(usm->timecheck))->tv_nsec
; if ((&timediff)->tv_nsec < 0) { (&timediff)->
tv_sec--; (&timediff)->tv_nsec += 1000000000L; } } while
(0)
;
184 usmcookie->time = usm->time + timediff.tv_sec;
185 } else
186 usmcookie->time = 0;
187 usmcookie->boots = usm->boots;
188
189 if (agent->v3->level & SNMP_MSGFLAG_AUTH0x01)
190 digestlen = usm_digestlen(usm->digest);
191 if (agent->v3->level & SNMP_MSGFLAG_PRIV0x02)
192 saltlen = sizeof(usmcookie->salt);
193
194 if ((params = ober_printf_elements(NULL((void *)0), "{xddxxx}", usm->engineid,
195 usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user,
196 usm->userlen, digest, digestlen, &(usmcookie->salt),
197 saltlen)) == NULL((void *)0)) {
198 free(usmcookie);
199 return NULL((void *)0);
200 }
201
202 if (ober_scanf_elements(params, "{SSSSe", &digestelm) == -1) {
203 ober_free_element(params);
204 free(usmcookie);
205 return NULL((void *)0);
206 }
207
208 ober_set_writecallback(digestelm, usm_digest_pos, usmcookie);
209
210 bzero(&ber, sizeof(ber));
211 ober_set_application(&ber, smi_application);
212 if (ober_write_elements(&ber, params) != -1)
213 berlen = ber_copy_writebuf(&ber, (void **)&secparams);
214
215 *len = berlen;
216 ober_free_element(params);
217 ober_free(&ber);
218 return secparams;
219}
220
221static struct ber_element *
222usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie)
223{
224 struct usm_sec *usm = agent->v3->sec->data;
225 struct usm_cookie *usmcookie = cookie;
226 struct ber ber;
227 struct ber_element *retpdu;
228 char *serialpdu, *encpdu;
229 ssize_t pdulen;
230 size_t encpdulen;
231
232 bzero(&ber, sizeof(ber));
233 ober_set_application(&ber, smi_application);
234 pdulen = ober_write_elements(&ber, pdu);
235 if (pdulen == -1)
236 return NULL((void *)0);
237
238 ober_get_writebuf(&ber, (void **)&serialpdu);
239
240 encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu,
241 pdulen, &encpdulen);
242 ober_free(&ber);
243 if (encpdu == NULL((void *)0))
244 return NULL((void *)0);
245
246 retpdu = ober_add_nstring(NULL((void *)0), encpdu, encpdulen);
247 free(encpdu);
248 return retpdu;
249}
250
251static char *
252usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key,
253 struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t *outlen)
254{
255 EVP_CIPHER_CTX *ctx;
256 size_t i;
257 char iv[EVP_MAX_IV_LENGTH16];
258 char *salt = (char *)&(cookie->salt);
259 char *outtext;
260 int len, len2, bs;
261 uint32_t ivv;
262
263 switch (EVP_CIPHER_type(cipher)) {
264 case NID_des_cbc31:
265 /* RFC3414, chap 8.1.1.1. */
266 for (i = 0; i < 8; i++)
267 iv[i] = salt[i] ^ key[USM_SALTOFFSET8 + i];
268 break;
269 case NID_aes_128_cfb128421:
270 /* RFC3826, chap 3.1.2.1. */
271 ivv = htobe32(cookie->boots)(__uint32_t)(__builtin_constant_p(cookie->boots) ? (__uint32_t
)(((__uint32_t)(cookie->boots) & 0xff) << 24 | (
(__uint32_t)(cookie->boots) & 0xff00) << 8 | ((__uint32_t
)(cookie->boots) & 0xff0000) >> 8 | ((__uint32_t
)(cookie->boots) & 0xff000000) >> 24) : __swap32md
(cookie->boots))
;
272 memcpy(iv, &ivv, sizeof(ivv));
273 ivv = htobe32(cookie->time)(__uint32_t)(__builtin_constant_p(cookie->time) ? (__uint32_t
)(((__uint32_t)(cookie->time) & 0xff) << 24 | ((
__uint32_t)(cookie->time) & 0xff00) << 8 | ((__uint32_t
)(cookie->time) & 0xff0000) >> 8 | ((__uint32_t)
(cookie->time) & 0xff000000) >> 24) : __swap32md
(cookie->time))
;
274 memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
275 memcpy(iv + 2 * sizeof(ivv), &(cookie->salt),
276 sizeof(cookie->salt));
277 break;
278 default:
279 return NULL((void *)0);
280 }
281
282 if ((ctx = EVP_CIPHER_CTX_new()) == NULL((void *)0))
283 return NULL((void *)0);
284
285 if (!EVP_CipherInit(ctx, cipher, key, iv, do_enc)) {
286 EVP_CIPHER_CTX_free(ctx);
287 return NULL((void *)0);
288 }
289
290 EVP_CIPHER_CTX_set_padding(ctx, do_enc);
291
292 bs = EVP_CIPHER_block_size(cipher);
293 /* Maximum output size */
294 *outlen = pdulen + (bs - (pdulen % bs));
295
296 if ((outtext = malloc(*outlen)) == NULL((void *)0)) {
297 EVP_CIPHER_CTX_free(ctx);
298 return NULL((void *)0);
299 }
300
301 if (EVP_CipherUpdate(ctx, outtext, &len, serialpdu, pdulen) &&
302 EVP_CipherFinal_ex(ctx, outtext + len, &len2))
303 *outlen = len + len2;
304 else {
305 free(outtext);
306 outtext = NULL((void *)0);
307 }
308
309 EVP_CIPHER_CTX_free(ctx);
310
311 return outtext;
312}
313
314static int
315usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
316 size_t secparamsoffset, void *cookie)
317{
318 struct usm_sec *usm = agent->v3->sec->data;
319 struct usm_cookie *usmcookie = cookie;
320 u_char digest[EVP_MAX_MD_SIZE64];
321
322 if ((agent->v3->level & SNMP_MSGFLAG_AUTH0x01) == 0)
323 return 0;
324
325 if (usm->authlevel != USM_KEY_LOCALIZED)
326 return -1;
327
328 if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
329 buflen, digest, NULL((void *)0)) == NULL((void *)0))
330 return -1;
331
332 memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
333 usm_digestlen(usm->digest));
334 return 0;
335}
336
337static int
338usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
339 off_t secparamsoffset, char *buf, size_t buflen, uint8_t level,
340 void **cookie)
341{
342 struct usm_sec *usm = agent->v3->sec->data;
343 struct ber ber;
344 struct ber_element *secparams;
345 char *engineid, *user, *digest, *salt;
346 size_t engineidlen, userlen, digestlen, saltlen;
347 struct timespec now, timediff;
348 off_t digestoffset;
349 char exp_digest[EVP_MAX_MD_SIZE64];
350 struct usm_cookie *usmcookie;
351
352 bzero(&ber, sizeof(ber));
353 bzero(exp_digest, sizeof(exp_digest));
354
355 ober_set_application(&ber, smi_application);
356 ober_set_readbuf(&ber, buf, buflen);
357 if ((secparams = ober_read_elements(&ber, NULL((void *)0))) == NULL((void *)0))
358 return -1;
359 ober_free(&ber);
360
361 if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL((void *)0))
362 goto fail;
363 *cookie = usmcookie;
364
365 if (ober_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen,
366 &(usmcookie->boots), &(usmcookie->time), &user, &userlen,
367 &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1)
368 goto fail;
369 if (saltlen != sizeof(usmcookie->salt) && saltlen != 0)
370 goto fail;
371 memcpy(&(usmcookie->salt), salt, saltlen);
372
373 if (!usm->engineidset) {
374 if (usm_setengineid(agent->v3->sec, engineid,
375 engineidlen) == -1)
376 goto fail;
377 } else {
378 if (usm->engineidlen != engineidlen)
379 goto fail;
380 if (memcmp(usm->engineid, engineid, engineidlen) != 0)
381 goto fail;
382 }
383
384 if (!usm->bootsset) {
385 usm->boots = usmcookie->boots;
386 usm->bootsset = 1;
387 } else {
388 if (usmcookie->boots < usm->boots)
389 goto fail;
390 if (usmcookie->boots > usm->boots) {
391 usm->bootsset = 0;
392 usm->timeset = 0;
393 usm_doinit(agent);
394 goto fail;
395 }
396 }
397
398 if (!usm->timeset) {
399 usm->time = usmcookie->time;
400 if (clock_gettime(CLOCK_MONOTONIC3, &usm->timecheck) == -1)
401 goto fail;
402 usm->timeset = 1;
403 } else {
404 if (clock_gettime(CLOCK_MONOTONIC3, &now) == -1)
405 goto fail;
406 timespecsub(&now, &(usm->timecheck), &timediff)do { (&timediff)->tv_sec = (&now)->tv_sec - (&
(usm->timecheck))->tv_sec; (&timediff)->tv_nsec =
(&now)->tv_nsec - (&(usm->timecheck))->tv_nsec
; if ((&timediff)->tv_nsec < 0) { (&timediff)->
tv_sec--; (&timediff)->tv_nsec += 1000000000L; } } while
(0)
;
407 if (usmcookie->time <
408 usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW150 ||
409 usmcookie->time >
410 usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW150) {
411 usm->bootsset = 0;
412 usm->timeset = 0;
413 usm_doinit(agent);
414 goto fail;
415 }
416 }
417 /*
418 * Don't assume these are set if both are zero.
419 * Ugly hack for HP Laserjet
420 */
421 if (usm->boots == 0 && usm->time == 0) {
422 usm->bootsset = 0;
423 usm->timeset = 0;
424 }
425
426 if (userlen != usm->userlen ||
427 memcmp(user, usm->user, userlen) != 0)
428 goto fail;
429
430 if (level & SNMP_MSGFLAG_AUTH0x01) {
431 if (digestlen != usm_digestlen(usm->digest))
432 goto fail;
433 }
434 if ((agent->v3->level & SNMP_MSGFLAG_AUTH0x01)) {
435 bzero(packet + secparamsoffset + digestoffset, digestlen);
436 if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet,
437 packetlen, exp_digest, NULL((void *)0)) == NULL((void *)0))
438 goto fail;
439
440 if (memcmp(exp_digest, digest, digestlen) != 0)
441 goto fail;
442 } else
443 if (digestlen != 0)
444 goto fail;
445
446 ober_free_element(secparams);
447 return 0;
448
449fail:
450 free(usmcookie);
451 ober_free_element(secparams);
452 return -1;
453}
454
455struct ber_element *
456usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie)
457{
458 struct usm_sec *usm = agent->v3->sec->data;
459 struct usm_cookie *usmcookie = cookie;
460 struct ber ber;
461 struct ber_element *scopedpdu;
462 char *rawpdu;
463 size_t rawpdulen;
464
465 if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie,
466 encpdu, encpdulen, &rawpdulen)) == NULL((void *)0))
467 return NULL((void *)0);
468
469 bzero(&ber, sizeof(ber));
470 ober_set_application(&ber, smi_application);
471 ober_set_readbuf(&ber, rawpdu, rawpdulen);
472 scopedpdu = ober_read_elements(&ber, NULL((void *)0));
473 ober_free(&ber);
474 free(rawpdu);
475
476 return scopedpdu;
477}
478
479static void
480usm_digest_pos(void *data, size_t offset)
481{
482 struct usm_cookie *usmcookie = data;
483
484 usmcookie->digestoffset = offset;
485}
486
487static void
488usm_free(void *data)
489{
490 struct usm_sec *usm = data;
491
492 free(usm->user);
493 free(usm->authkey);
494 free(usm->privkey);
495 free(usm->engineid);
496 free(usm);
497}
498
499int
500usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
501 size_t keylen, enum usm_key_level level)
502{
503 struct usm_sec *usm = sec->data;
504 char *lkey;
505
506 /*
507 * We could transform a master key to a local key here if we already
508 * have usm_setengineid called. Sine snmpc.c is the only caller at
509 * the moment there's no need, since it always calls this function
510 * first.
511 */
512 if (level == USM_KEY_PASSWORD) {
513 if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL((void *)0))
514 return -1;
515 level = USM_KEY_MASTER;
516 keylen = EVP_MD_size(digest);
Value stored to 'keylen' is never read
517 } else {
518 if (keylen != (size_t)EVP_MD_size(digest)) {
519 errno(*__errno()) = EINVAL22;
520 return -1;
521 }
522 if ((lkey = malloc(keylen)) == NULL((void *)0))
523 return -1;
524 memcpy(lkey, key, keylen);
525 usm->authkey = lkey;
526 }
527 usm->digest = digest;
528 usm->authlevel = level;
529 return 0;
530}
531
532int
533usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key,
534 size_t keylen, enum usm_key_level level)
535{
536 struct usm_sec *usm = sec->data;
537 char *lkey;
538
539 if (usm->digest == NULL((void *)0)) {
540 errno(*__errno()) = EINVAL22;
541 return -1;
542 }
543
544 /*
545 * We could transform a master key to a local key here if we already
546 * have usm_setengineid called. Sine snmpc.c is the only caller at
547 * the moment there's no need, since it always calls us first.
548 */
549 if (level == USM_KEY_PASSWORD) {
550 if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL((void *)0))
551 return -1;
552 level = USM_KEY_MASTER;
553 keylen = EVP_MD_size(usm->digest);
554 } else {
555 if (keylen != (size_t)EVP_MD_size(usm->digest)) {
556 errno(*__errno()) = EINVAL22;
557 return -1;
558 }
559 if ((lkey = malloc(keylen)) == NULL((void *)0))
560 return -1;
561 memcpy(lkey, key, keylen);
562 usm->privkey = lkey;
563 }
564 usm->cipher = cipher;
565 usm->privlevel = level;
566 return 0;
567}
568
569int
570usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
571{
572 struct usm_sec *usm = sec->data;
573 char *mkey;
574
575 if (usm->engineid != NULL((void *)0))
576 free(usm->engineid);
577 if ((usm->engineid = malloc(engineidlen)) == NULL((void *)0))
578 return -1;
579 memcpy(usm->engineid, engineid, engineidlen);
580 usm->engineidlen = engineidlen;
581 usm->engineidset = 1;
582
583 if (usm->authlevel == USM_KEY_MASTER) {
584 mkey = usm->authkey;
585 if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
586 mkey)) == NULL((void *)0)) {
587 usm->authkey = mkey;
588 return -1;
589 }
590 free(mkey);
591 usm->authlevel = USM_KEY_LOCALIZED;
592 }
593 if (usm->privlevel == USM_KEY_MASTER) {
594 mkey = usm->privkey;
595 if ((usm->privkey = usm_mkey2lkey(usm, usm->digest,
596 mkey)) == NULL((void *)0)) {
597 usm->privkey = mkey;
598 return -1;
599 }
600 free(mkey);
601 usm->privlevel = USM_KEY_LOCALIZED;
602 }
603
604 return 0;
605}
606
607int
608usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
609{
610 struct usm_sec *usm = sec->data;
611
612 if (clock_gettime(CLOCK_MONOTONIC3, &(usm->timecheck)) == -1)
613 return -1;
614
615 usm->boots = boots;
616 usm->bootsset = 1;
617 usm->time = time;
618 usm->timeset = 1;
619 return 0;
620}
621
622static char *
623usm_passwd2mkey(const EVP_MD *md, const char *passwd)
624{
625 EVP_MD_CTX *ctx;
626 int i, count;
627 const u_char *pw;
628 u_char *c;
629 u_char keybuf[EVP_MAX_MD_SIZE64];
630 unsigned dlen;
631 char *key;
632
633 if ((ctx = EVP_MD_CTX_new()) == NULL((void *)0))
634 return NULL((void *)0);
635 if (!EVP_DigestInit_ex(ctx, md, NULL((void *)0))) {
636 EVP_MD_CTX_free(ctx);
637 return NULL((void *)0);
638 }
639
640 pw = (const u_char *)passwd;
641 for (count = 0; count < 1048576; count += 64) {
642 c = keybuf;
643 for (i = 0; i < 64; i++) {
644 if (*pw == '\0')
645 pw = (const u_char *)passwd;
646 *c++ = *pw++;
647 }
648 if (!EVP_DigestUpdate(ctx, keybuf, 64)) {
649 EVP_MD_CTX_free(ctx);
650 return NULL((void *)0);
651 }
652 }
653 if (!EVP_DigestFinal_ex(ctx, keybuf, &dlen)) {
654 EVP_MD_CTX_free(ctx);
655 return NULL((void *)0);
656 }
657 EVP_MD_CTX_free(ctx);
658
659 if ((key = malloc(dlen)) == NULL((void *)0))
660 return NULL((void *)0);
661 memcpy(key, keybuf, dlen);
662 return key;
663}
664
665static char *
666usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
667{
668 EVP_MD_CTX *ctx;
669 u_char buf[EVP_MAX_MD_SIZE64];
670 u_char *lkey;
671 unsigned lklen;
672
673 if ((ctx = EVP_MD_CTX_new()) == NULL((void *)0))
674 return NULL((void *)0);
675
676 if (!EVP_DigestInit_ex(ctx, md, NULL((void *)0)) ||
677 !EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md)) ||
678 !EVP_DigestUpdate(ctx, usm->engineid, usm->engineidlen) ||
679 !EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md)) ||
680 !EVP_DigestFinal_ex(ctx, buf, &lklen)) {
681 EVP_MD_CTX_free(ctx);
682 return NULL((void *)0);
683 }
684
685 EVP_MD_CTX_free(ctx);
686
687 if ((lkey = malloc(lklen)) == NULL((void *)0))
688 return NULL((void *)0);
689 memcpy(lkey, buf, lklen);
690 return lkey;
691}
692
693static size_t
694usm_digestlen(const EVP_MD *md)
695{
696 switch (EVP_MD_type(md)) {
697 case NID_md54:
698 case NID_sha164:
699 return 12;
700 case NID_sha224675:
701 return 16;
702 case NID_sha256672:
703 return 24;
704 case NID_sha384673:
705 return 32;
706 case NID_sha512674:
707 return 48;
708 default:
709 return 0;
710 }
711}