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' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
40 | SLIST_HEAD(, usmuser)struct { struct usmuser *slh_first; } usmuserlist; |
41 | |
42 | const EVP_MD *usm_get_md(enum usmauth); |
43 | size_t usm_get_digestlen(enum usmauth); |
44 | const EVP_CIPHER *usm_get_cipher(enum usmpriv); |
45 | int usm_valid_digestlen(size_t digestlen); |
46 | void usm_cb_digest(void *, size_t); |
47 | int usm_valid_digest(struct snmp_message *, off_t, char *, |
48 | size_t); |
49 | struct ber_element *usm_decrypt(struct snmp_message *, |
50 | struct ber_element *); |
51 | ssize_t usm_crypt(struct snmp_message *, u_char *, int, |
52 | u_char *, int); |
53 | char *usm_passwd2key(const EVP_MD *, char *, int *); |
54 | |
55 | void |
56 | usm_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 | |
87 | const EVP_MD * |
88 | usm_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 | |
109 | size_t |
110 | usm_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 | |
130 | const EVP_CIPHER * |
131 | usm_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 | |
144 | int |
145 | usm_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 | |
160 | struct usmuser * |
161 | usm_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 | |
175 | const struct usmuser * |
176 | usm_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 | |
196 | struct usmuser * |
197 | usm_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 | |
208 | int |
209 | usm_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 | |
253 | fail: |
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 | |
262 | struct ber_element * |
263 | usm_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 | |
389 | done: |
390 | ober_free(&ber); |
391 | if (usm != NULL((void *)0)) |
392 | ober_free_elements(usm); |
393 | return next; |
394 | } |
395 | |
396 | struct ber_element * |
397 | usm_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 | |
463 | done: |
464 | ober_free(&ber); |
465 | ober_free_elements(usm); |
466 | return res; |
467 | } |
468 | |
469 | void |
470 | usm_cb_digest(void *arg, size_t offs) |
471 | { |
472 | struct snmp_message *msg = arg; |
473 | msg->sm_digest_offs += offs; |
474 | } |
475 | |
476 | struct ber_element * |
477 | usm_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 | */ |
512 | void |
513 | usm_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 | |
545 | void |
546 | usm_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 | |
551 | int |
552 | usm_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 | |
584 | struct ber_element * |
585 | usm_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 | |
621 | ssize_t |
622 | usm_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 | */ |
683 | char * |
684 | usm_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 | } |