File: | src/usr.bin/snmp/usm.c |
Warning: | line 516, column 3 Value stored to 'keylen' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
37 | struct 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 | |
57 | struct usm_cookie { |
58 | size_t digestoffset; |
59 | long long salt; |
60 | uint32_t boots; |
61 | uint32_t time; |
62 | }; |
63 | |
64 | static int usm_doinit(struct snmp_agent *); |
65 | static char *usm_genparams(struct snmp_agent *, size_t *, void **); |
66 | static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *); |
67 | static struct ber_element *usm_encpdu(struct snmp_agent *agent, |
68 | struct ber_element *pdu, void *cookie); |
69 | static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *, |
70 | char *, size_t, size_t *); |
71 | static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *, |
72 | size_t, uint8_t, void **); |
73 | struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *); |
74 | static void usm_digest_pos(void *, size_t); |
75 | static void usm_free(void *); |
76 | static char *usm_passwd2mkey(const EVP_MD *, const char *); |
77 | static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *); |
78 | static size_t usm_digestlen(const EVP_MD *); |
79 | |
80 | struct snmp_sec * |
81 | usm_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 | |
119 | static int |
120 | usm_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 | |
158 | static char * |
159 | usm_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 | |
221 | static struct ber_element * |
222 | usm_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 | |
251 | static char * |
252 | usm_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 | |
314 | static int |
315 | usm_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 | |
337 | static int |
338 | usm_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 | |
449 | fail: |
450 | free(usmcookie); |
451 | ober_free_element(secparams); |
452 | return -1; |
453 | } |
454 | |
455 | struct ber_element * |
456 | usm_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 | |
479 | static void |
480 | usm_digest_pos(void *data, size_t offset) |
481 | { |
482 | struct usm_cookie *usmcookie = data; |
483 | |
484 | usmcookie->digestoffset = offset; |
485 | } |
486 | |
487 | static void |
488 | usm_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 | |
499 | int |
500 | usm_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 | |
532 | int |
533 | usm_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 | |
569 | int |
570 | usm_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 | |
607 | int |
608 | usm_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 | |
622 | static char * |
623 | usm_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 | |
665 | static char * |
666 | usm_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 | |
693 | static size_t |
694 | usm_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 | } |