File: | src/usr.sbin/snmpd/usm.c |
Warning: | line 473, 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.24 2022/01/05 17:01:06 tb 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 | #include <sys/stat.h> |
22 | #include <sys/socket.h> |
23 | #include <sys/un.h> |
24 | #include <sys/tree.h> |
25 | |
26 | #include <net/if.h> |
27 | |
28 | #include <errno(*__errno()).h> |
29 | #include <event.h> |
30 | #include <fcntl.h> |
31 | #include <stdlib.h> |
32 | #include <string.h> |
33 | #include <unistd.h> |
34 | #include <signal.h> |
35 | #ifdef DEBUG |
36 | #include <assert.h> |
37 | #endif |
38 | |
39 | #include <openssl/evp.h> |
40 | #include <openssl/hmac.h> |
41 | |
42 | #include "snmpd.h" |
43 | #include "mib.h" |
44 | |
45 | SLIST_HEAD(, usmuser)struct { struct usmuser *slh_first; } usmuserlist; |
46 | |
47 | const EVP_MD *usm_get_md(enum usmauth); |
48 | size_t usm_get_digestlen(enum usmauth); |
49 | const EVP_CIPHER *usm_get_cipher(enum usmpriv); |
50 | int usm_valid_digestlen(size_t digestlen); |
51 | void usm_cb_digest(void *, size_t); |
52 | int usm_valid_digest(struct snmp_message *, off_t, char *, |
53 | size_t); |
54 | struct ber_element *usm_decrypt(struct snmp_message *, |
55 | struct ber_element *); |
56 | ssize_t usm_crypt(struct snmp_message *, u_char *, int, |
57 | u_char *, int); |
58 | char *usm_passwd2key(const EVP_MD *, char *, int *); |
59 | |
60 | void |
61 | usm_generate_keys(void) |
62 | { |
63 | struct usmuser *up; |
64 | const EVP_MD *md; |
65 | char *key; |
66 | int len; |
67 | |
68 | SLIST_FOREACH(up, &usmuserlist, uu_next)for((up) = ((&usmuserlist)->slh_first); (up) != ((void *)0); (up) = ((up)->uu_next.sle_next)) { |
69 | if ((md = usm_get_md(up->uu_auth)) == NULL((void *)0)) |
70 | continue; |
71 | |
72 | /* convert auth password to key */ |
73 | len = 0; |
74 | key = usm_passwd2key(md, up->uu_authkey, &len); |
75 | free(up->uu_authkey); |
76 | up->uu_authkey = key; |
77 | up->uu_authkeylen = len; |
78 | |
79 | /* optionally convert privacy password to key */ |
80 | if (up->uu_priv != PRIV_NONE) { |
81 | arc4random_buf(&up->uu_salt, sizeof(up->uu_salt)); |
82 | |
83 | len = SNMP_CIPHER_KEYLEN16; |
84 | key = usm_passwd2key(md, up->uu_privkey, &len); |
85 | free(up->uu_privkey); |
86 | up->uu_privkey = key; |
87 | } |
88 | } |
89 | return; |
90 | } |
91 | |
92 | const EVP_MD * |
93 | usm_get_md(enum usmauth ua) |
94 | { |
95 | switch (ua) { |
96 | case AUTH_MD5: |
97 | return EVP_md5(); |
98 | case AUTH_SHA1: |
99 | return EVP_sha1(); |
100 | case AUTH_SHA224: |
101 | return EVP_sha224(); |
102 | case AUTH_SHA256: |
103 | return EVP_sha256(); |
104 | case AUTH_SHA384: |
105 | return EVP_sha384(); |
106 | case AUTH_SHA512: |
107 | return EVP_sha512(); |
108 | case AUTH_NONE: |
109 | default: |
110 | return NULL((void *)0); |
111 | } |
112 | } |
113 | |
114 | size_t |
115 | usm_get_digestlen(enum usmauth ua) |
116 | { |
117 | switch (ua) { |
118 | case AUTH_MD5: |
119 | case AUTH_SHA1: |
120 | return 12; |
121 | case AUTH_SHA224: |
122 | return 16; |
123 | case AUTH_SHA256: |
124 | return 24; |
125 | case AUTH_SHA384: |
126 | return 32; |
127 | case AUTH_SHA512: |
128 | return 48; |
129 | case AUTH_NONE: |
130 | default: |
131 | return 0; |
132 | } |
133 | } |
134 | |
135 | const EVP_CIPHER * |
136 | usm_get_cipher(enum usmpriv up) |
137 | { |
138 | switch (up) { |
139 | case PRIV_DES: |
140 | return EVP_des_cbc(); |
141 | case PRIV_AES: |
142 | return EVP_aes_128_cfb128(); |
143 | case PRIV_NONE: |
144 | default: |
145 | return NULL((void *)0); |
146 | } |
147 | } |
148 | |
149 | int |
150 | usm_valid_digestlen(size_t digestlen) |
151 | { |
152 | switch (digestlen) { |
153 | case 0: |
154 | case 12: |
155 | case 16: |
156 | case 24: |
157 | case 32: |
158 | case 48: |
159 | return 1; |
160 | default: |
161 | return 0; |
162 | } |
163 | } |
164 | |
165 | struct usmuser * |
166 | usm_newuser(char *name, const char **errp) |
167 | { |
168 | struct usmuser *up = usm_finduser(name); |
169 | if (up != NULL((void *)0)) { |
170 | *errp = "user redefined"; |
171 | return NULL((void *)0); |
172 | } |
173 | if ((up = calloc(1, sizeof(*up))) == NULL((void *)0)) |
174 | fatal("usm"); |
175 | up->uu_name = name; |
176 | SLIST_INSERT_HEAD(&usmuserlist, up, uu_next)do { (up)->uu_next.sle_next = (&usmuserlist)->slh_first ; (&usmuserlist)->slh_first = (up); } while (0); |
177 | return up; |
178 | } |
179 | |
180 | const struct usmuser * |
181 | usm_check_mincred(int minlevel, const char **errstr) |
182 | { |
183 | struct usmuser *up; |
184 | |
185 | if (minlevel == 0) |
186 | return NULL((void *)0); |
187 | |
188 | SLIST_FOREACH(up, &usmuserlist, uu_next)for((up) = ((&usmuserlist)->slh_first); (up) != ((void *)0); (up) = ((up)->uu_next.sle_next)) { |
189 | if (minlevel & SNMP_MSGFLAG_PRIV0x02 && up->uu_privkey == NULL((void *)0)) { |
190 | *errstr = "missing enckey"; |
191 | return up; |
192 | } |
193 | if (minlevel & SNMP_MSGFLAG_AUTH0x01 && up->uu_authkey == NULL((void *)0)) { |
194 | *errstr = "missing authkey"; |
195 | return up; |
196 | } |
197 | } |
198 | return NULL((void *)0); |
199 | } |
200 | |
201 | struct usmuser * |
202 | usm_finduser(char *name) |
203 | { |
204 | struct usmuser *up; |
205 | |
206 | SLIST_FOREACH(up, &usmuserlist, uu_next)for((up) = ((&usmuserlist)->slh_first); (up) != ((void *)0); (up) = ((up)->uu_next.sle_next)) { |
207 | if (!strcmp(up->uu_name, name)) |
208 | return up; |
209 | } |
210 | return NULL((void *)0); |
211 | } |
212 | |
213 | int |
214 | usm_checkuser(struct usmuser *up, const char **errp) |
215 | { |
216 | char *auth = NULL((void *)0), *priv = NULL((void *)0); |
217 | |
218 | if (up->uu_auth != AUTH_NONE && up->uu_authkey == NULL((void *)0)) { |
219 | *errp = "missing auth passphrase"; |
220 | goto fail; |
221 | } else if (up->uu_auth == AUTH_NONE && up->uu_authkey != NULL((void *)0)) |
222 | up->uu_auth = AUTH_DEFAULTAUTH_SHA1; |
223 | |
224 | if (up->uu_priv != PRIV_NONE && up->uu_privkey == NULL((void *)0)) { |
225 | *errp = "missing priv passphrase"; |
226 | goto fail; |
227 | } else if (up->uu_priv == PRIV_NONE && up->uu_privkey != NULL((void *)0)) |
228 | up->uu_priv = PRIV_DEFAULTPRIV_AES; |
229 | |
230 | if (up->uu_auth == AUTH_NONE && up->uu_priv != PRIV_NONE) { |
231 | /* Standard prohibits noAuthPriv */ |
232 | *errp = "auth is mandatory with enc"; |
233 | goto fail; |
234 | } |
235 | |
236 | switch (up->uu_auth) { |
237 | case AUTH_NONE: |
238 | auth = "none"; |
239 | break; |
240 | case AUTH_MD5: |
241 | up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01; |
242 | auth = "HMAC-MD5-96"; |
243 | break; |
244 | case AUTH_SHA1: |
245 | up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01; |
246 | auth = "HMAC-SHA1-96"; |
247 | break; |
248 | case AUTH_SHA224: |
249 | up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01; |
250 | auth = "usmHMAC128SHA224AuthProtocol"; |
251 | case AUTH_SHA256: |
252 | up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01; |
253 | auth = "usmHMAC192SHA256AuthProtocol"; |
254 | case AUTH_SHA384: |
255 | up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01; |
256 | auth = "usmHMAC256SHA384AuthProtocol"; |
257 | case AUTH_SHA512: |
258 | up->uu_seclevel |= SNMP_MSGFLAG_AUTH0x01; |
259 | auth = "usmHMAC384SHA512AuthProtocol"; |
260 | } |
261 | |
262 | switch (up->uu_priv) { |
263 | case PRIV_NONE: |
264 | priv = "none"; |
265 | break; |
266 | case PRIV_DES: |
267 | up->uu_seclevel |= SNMP_MSGFLAG_PRIV0x02; |
268 | priv = "CBC-DES"; |
269 | break; |
270 | case PRIV_AES: |
271 | up->uu_seclevel |= SNMP_MSGFLAG_PRIV0x02; |
272 | priv = "CFB128-AES-128"; |
273 | break; |
274 | } |
275 | |
276 | log_debug("user \"%s\" auth %s enc %s", up->uu_name, auth, priv); |
277 | return 0; |
278 | |
279 | fail: |
280 | free(up->uu_name); |
281 | free(up->uu_authkey); |
282 | free(up->uu_privkey); |
283 | 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); |
284 | free(up); |
285 | return -1; |
286 | } |
287 | |
288 | struct ber_element * |
289 | usm_decode(struct snmp_message *msg, struct ber_element *elm, const char **errp) |
290 | { |
291 | struct snmp_stats *stats = &snmpd_env->sc_stats; |
292 | off_t offs, offs2; |
293 | char *usmparams; |
294 | size_t len; |
295 | size_t enginelen, userlen, digestlen, saltlen; |
296 | struct ber ber; |
297 | struct ber_element *usm = NULL((void *)0), *next = NULL((void *)0), *decr; |
298 | char *engineid; |
299 | char *user; |
300 | char *digest, *salt; |
301 | u_long now; |
302 | long long engine_boots, engine_time; |
303 | |
304 | bzero(&ber, sizeof(ber)); |
305 | offs = ober_getpos(elm); |
306 | |
307 | if (ober_get_nstring(elm, (void *)&usmparams, &len) < 0) { |
308 | *errp = "cannot decode security params"; |
309 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
310 | goto done; |
311 | } |
312 | |
313 | ober_set_readbuf(&ber, usmparams, len); |
314 | usm = ober_read_elements(&ber, NULL((void *)0)); |
315 | if (usm == NULL((void *)0)) { |
316 | *errp = "cannot decode security params"; |
317 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
318 | goto done; |
319 | } |
320 | |
321 | #ifdef DEBUG |
322 | fprintf(stderr(&__sF[2]), "decode USM parameters:\n"); |
323 | smi_debug_elements(usm); |
324 | #endif |
325 | |
326 | if (ober_scanf_elements(usm, "{xiixpxx$", &engineid, &enginelen, |
327 | &engine_boots, &engine_time, &user, &userlen, &offs2, |
328 | &digest, &digestlen, &salt, &saltlen) != 0) { |
329 | *errp = "cannot decode USM params"; |
330 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
331 | goto done; |
332 | } |
333 | |
334 | log_debug("USM: engineid '%s', engine boots %lld, engine time %lld, " |
335 | "user '%s'", tohexstr(engineid, enginelen), engine_boots, |
336 | engine_time, user); |
337 | |
338 | if (enginelen > SNMPD_MAXENGINEIDLEN32 || |
339 | userlen > SNMPD_MAXUSERNAMELEN32 || |
340 | !usm_valid_digestlen(digestlen) || |
341 | (saltlen != (MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0) ? SNMP_USM_SALTLEN8 : 0))) { |
342 | *errp = "bad field length"; |
343 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
344 | goto done; |
345 | } |
346 | |
347 | if (enginelen != snmpd_env->sc_engineid_len || |
348 | memcmp(engineid, snmpd_env->sc_engineid, enginelen) != 0) { |
349 | *errp = "unknown engine id"; |
350 | msg->sm_usmerr = OIDVAL_usmErrEngineId4; |
351 | stats->snmp_usmnosuchengine++; |
352 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
353 | goto done; |
354 | } |
355 | |
356 | msg->sm_engine_boots = (u_int32_t)engine_boots; |
357 | msg->sm_engine_time = (u_int32_t)engine_time; |
358 | |
359 | memcpy(msg->sm_username, user, userlen); |
360 | msg->sm_username[userlen] = '\0'; |
361 | msg->sm_user = usm_finduser(msg->sm_username); |
362 | if (msg->sm_user == NULL((void *)0)) { |
363 | *errp = "no such user"; |
364 | msg->sm_usmerr = OIDVAL_usmErrUserName3; |
365 | stats->snmp_usmnosuchuser++; |
366 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
367 | goto done; |
368 | } |
369 | if (MSG_SECLEVEL(msg)((msg)->sm_flags & (0x01 | 0x02)) > msg->sm_user->uu_seclevel) { |
370 | *errp = "unsupported security model"; |
371 | msg->sm_usmerr = OIDVAL_usmErrSecLevel1; |
372 | stats->snmp_usmbadseclevel++; |
373 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
374 | goto done; |
375 | } |
376 | |
377 | /* |
378 | * offs is the offset of the USM string within the serialized msg |
379 | * and offs2 the offset of the digest within the USM string. |
380 | */ |
381 | if (!usm_valid_digest(msg, offs + offs2, digest, digestlen)) { |
382 | *errp = "bad msg digest"; |
383 | msg->sm_usmerr = OIDVAL_usmErrDigest5; |
384 | stats->snmp_usmwrongdigest++; |
385 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
386 | goto done; |
387 | } |
388 | |
389 | if (MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0)) { |
390 | memcpy(msg->sm_salt, salt, saltlen); |
391 | if ((decr = usm_decrypt(msg, elm->be_next)) == NULL((void *)0)) { |
392 | *errp = "cannot decrypt msg"; |
393 | msg->sm_usmerr = OIDVAL_usmErrDecrypt6; |
394 | stats->snmp_usmdecrypterr++; |
395 | msg->sm_flags &= SNMP_MSGFLAG_REPORT0x04; |
396 | goto done; |
397 | } |
398 | ober_replace_elements(elm, decr); |
399 | } |
400 | |
401 | now = snmpd_engine_time(); |
402 | if (engine_boots != snmpd_env->sc_engine_boots || |
403 | engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW150) || |
404 | engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW150)) { |
405 | *errp = "out of time window"; |
406 | msg->sm_usmerr = OIDVAL_usmErrTimeWindow2; |
407 | stats->snmp_usmtimewindow++; |
408 | goto done; |
409 | } |
410 | |
411 | next = elm->be_next; |
412 | |
413 | done: |
414 | ober_free(&ber); |
415 | if (usm != NULL((void *)0)) |
416 | ober_free_elements(usm); |
417 | return next; |
418 | } |
419 | |
420 | struct ber_element * |
421 | usm_encode(struct snmp_message *msg, struct ber_element *e) |
422 | { |
423 | struct ber ber; |
424 | struct ber_element *usm, *a, *res = NULL((void *)0); |
425 | void *ptr; |
426 | char digest[SNMP_USM_MAXDIGESTLEN48]; |
427 | size_t digestlen, saltlen; |
428 | ssize_t len; |
429 | |
430 | msg->sm_digest_offs = 0; |
431 | bzero(&ber, sizeof(ber)); |
432 | |
433 | usm = ober_add_sequence(NULL((void *)0)); |
434 | |
435 | if (MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0)) { |
436 | /* |
437 | * Fill in enough zeroes and remember the position within the |
438 | * messages. The digest will be calculated once the message |
439 | * is complete. |
440 | */ |
441 | #ifdef DEBUG |
442 | assert(msg->sm_user != NULL((void *)0)); |
443 | #endif |
444 | bzero(digest, sizeof(digest)); |
445 | digestlen = usm_get_digestlen(msg->sm_user->uu_auth); |
446 | } else |
447 | digestlen = 0; |
448 | |
449 | if (MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0)) { |
450 | #ifdef DEBUG |
451 | assert(msg->sm_user != NULL((void *)0)); |
452 | #endif |
453 | ++(msg->sm_user->uu_salt); |
454 | memcpy(msg->sm_salt, &msg->sm_user->uu_salt, |
455 | sizeof(msg->sm_salt)); |
456 | saltlen = sizeof(msg->sm_salt); |
457 | } else |
458 | saltlen = 0; |
459 | |
460 | msg->sm_engine_boots = (u_int32_t)snmpd_env->sc_engine_boots; |
461 | msg->sm_engine_time = (u_int32_t)snmpd_engine_time(); |
462 | if ((a = ober_printf_elements(usm, "xdds", |
463 | snmpd_env->sc_engineid, snmpd_env->sc_engineid_len, |
464 | msg->sm_engine_boots, msg->sm_engine_time, |
465 | msg->sm_username)) == NULL((void *)0)) |
466 | goto done; |
467 | |
468 | if ((a = ober_add_nstring(a, digest, digestlen)) == NULL((void *)0)) |
469 | goto done; |
470 | if (digestlen > 0) |
471 | ober_set_writecallback(a, usm_cb_digest, msg); |
472 | |
473 | 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' | |
474 | goto done; |
475 | |
476 | #ifdef DEBUG |
477 | fprintf(stderr(&__sF[2]), "encode USM parameters:\n"); |
478 | smi_debug_elements(usm); |
479 | #endif |
480 | len = ober_write_elements(&ber, usm); |
481 | if (ober_get_writebuf(&ber, &ptr) > 0) { |
482 | res = ober_add_nstring(e, (char *)ptr, len); |
483 | if (digestlen > 0) |
484 | ober_set_writecallback(res, usm_cb_digest, msg); |
485 | } |
486 | |
487 | done: |
488 | ober_free(&ber); |
489 | ober_free_elements(usm); |
490 | return res; |
491 | } |
492 | |
493 | void |
494 | usm_cb_digest(void *arg, size_t offs) |
495 | { |
496 | struct snmp_message *msg = arg; |
497 | msg->sm_digest_offs += offs; |
498 | } |
499 | |
500 | struct ber_element * |
501 | usm_encrypt(struct snmp_message *msg, struct ber_element *pdu) |
502 | { |
503 | struct ber ber; |
504 | struct ber_element *encrpdu = NULL((void *)0); |
505 | void *ptr; |
506 | ssize_t elen, len; |
507 | u_char encbuf[READ_BUF_SIZE65535]; |
508 | |
509 | if (!MSG_HAS_PRIV(msg)(((msg)->sm_flags & 0x02) != 0)) |
510 | return pdu; |
511 | |
512 | bzero(&ber, sizeof(ber)); |
513 | |
514 | #ifdef DEBUG |
515 | fprintf(stderr(&__sF[2]), "encrypted PDU:\n"); |
516 | smi_debug_elements(pdu); |
517 | #endif |
518 | |
519 | len = ober_write_elements(&ber, pdu); |
520 | if (ober_get_writebuf(&ber, &ptr) > 0) { |
521 | elen = usm_crypt(msg, ptr, len, encbuf, 1); |
522 | if (elen > 0) |
523 | encrpdu = ober_add_nstring(NULL((void *)0), (char *)encbuf, elen); |
524 | } |
525 | |
526 | ober_free(&ber); |
527 | ober_free_elements(pdu); |
528 | return encrpdu; |
529 | } |
530 | |
531 | /* |
532 | * Calculate message digest and replace within message |
533 | */ |
534 | void |
535 | usm_finalize_digest(struct snmp_message *msg, char *buf, ssize_t len) |
536 | { |
537 | const EVP_MD *md; |
538 | u_char digest[EVP_MAX_MD_SIZE64]; |
539 | size_t digestlen; |
540 | unsigned hlen; |
541 | |
542 | if (msg->sm_resp == NULL((void *)0) || |
543 | !MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0) || |
544 | msg->sm_user == NULL((void *)0) || |
545 | msg->sm_digest_offs == 0 || |
546 | len <= 0) |
547 | return; |
548 | |
549 | if ((digestlen = usm_get_digestlen(msg->sm_user->uu_auth)) == 0) |
550 | return; |
551 | bzero(digest, digestlen); |
552 | #ifdef DEBUG |
553 | assert(msg->sm_digest_offs + digestlen <= (size_t)len); |
554 | assert(!memcmp(buf + msg->sm_digest_offs, digest, digestlen)); |
555 | #endif |
556 | |
557 | if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL((void *)0)) |
558 | return; |
559 | |
560 | HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen, |
561 | (u_char*)buf, (size_t)len, digest, &hlen); |
562 | |
563 | memcpy(buf + msg->sm_digest_offs, digest, digestlen); |
564 | return; |
565 | } |
566 | |
567 | void |
568 | usm_make_report(struct snmp_message *msg) |
569 | { |
570 | struct ber_oid usmstat = OID(MIB_usmStats, 0, 0){ { 1, 3, 6, 1, 6, 3, 15, 1, 1, 0, 0 } }; |
571 | |
572 | msg->sm_pdutype = SNMP_C_REPORT; |
573 | usmstat.bo_id[OIDIDX_usmStats9] = msg->sm_usmerr; |
574 | usmstat.bo_n = OIDIDX_usmStats9 + 2; |
575 | if (msg->sm_varbindresp != NULL((void *)0)) |
576 | ober_free_elements(msg->sm_varbindresp); |
577 | msg->sm_varbindresp = ober_add_sequence(NULL((void *)0)); |
578 | mps_getreq(NULL((void *)0), msg->sm_varbindresp, &usmstat, msg->sm_version); |
579 | return; |
580 | } |
581 | |
582 | int |
583 | usm_valid_digest(struct snmp_message *msg, off_t offs, |
584 | char *digest, size_t digestlen) |
585 | { |
586 | const EVP_MD *md; |
587 | u_char exp_digest[EVP_MAX_MD_SIZE64]; |
588 | unsigned hlen; |
589 | |
590 | if (!MSG_HAS_AUTH(msg)(((msg)->sm_flags & 0x01) != 0)) |
591 | return 1; |
592 | |
593 | if (digestlen != usm_get_digestlen(msg->sm_user->uu_auth)) |
594 | return 0; |
595 | |
596 | #ifdef DEBUG |
597 | assert(offs + digestlen <= msg->sm_datalen); |
598 | assert(bcmp(&msg->sm_data[offs], digest, digestlen) == 0); |
599 | #endif |
600 | |
601 | if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL((void *)0)) |
602 | return 0; |
603 | |
604 | memset(&msg->sm_data[offs], 0, digestlen); |
605 | HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen, |
606 | msg->sm_data, msg->sm_datalen, exp_digest, &hlen); |
607 | /* we don't bother to restore the original message */ |
608 | |
609 | if (hlen < digestlen) |
610 | return 0; |
611 | |
612 | return memcmp(digest, exp_digest, digestlen) == 0; |
613 | } |
614 | |
615 | struct ber_element * |
616 | usm_decrypt(struct snmp_message *msg, struct ber_element *encr) |
617 | { |
618 | u_char *privstr; |
619 | size_t privlen; |
620 | u_char buf[READ_BUF_SIZE65535]; |
621 | struct ber ber; |
622 | struct ber_element *scoped_pdu = NULL((void *)0); |
623 | ssize_t scoped_pdu_len; |
624 | |
625 | if (ober_get_nstring(encr, (void *)&privstr, &privlen) < 0) |
626 | return NULL((void *)0); |
627 | |
628 | scoped_pdu_len = usm_crypt(msg, privstr, (int)privlen, buf, 0); |
629 | if (scoped_pdu_len < 0) |
630 | return NULL((void *)0); |
631 | |
632 | bzero(&ber, sizeof(ber)); |
633 | ober_set_application(&ber, smi_application); |
634 | ober_set_readbuf(&ber, buf, scoped_pdu_len); |
635 | scoped_pdu = ober_read_elements(&ber, NULL((void *)0)); |
636 | |
637 | #ifdef DEBUG |
638 | if (scoped_pdu != NULL((void *)0)) { |
639 | fprintf(stderr(&__sF[2]), "decrypted scoped PDU:\n"); |
640 | smi_debug_elements(scoped_pdu); |
641 | } |
642 | #endif |
643 | |
644 | ober_free(&ber); |
645 | return scoped_pdu; |
646 | } |
647 | |
648 | ssize_t |
649 | usm_crypt(struct snmp_message *msg, u_char *inbuf, int inlen, u_char *outbuf, |
650 | int do_encrypt) |
651 | { |
652 | const EVP_CIPHER *cipher; |
653 | EVP_CIPHER_CTX *ctx; |
654 | u_char *privkey; |
655 | int i; |
656 | u_char iv[EVP_MAX_IV_LENGTH16]; |
657 | int len, len2; |
658 | int rv; |
659 | u_int32_t ivv; |
660 | |
661 | if ((cipher = usm_get_cipher(msg->sm_user->uu_priv)) == NULL((void *)0)) |
662 | return -1; |
663 | |
664 | privkey = (u_char *)msg->sm_user->uu_privkey; |
665 | #ifdef DEBUG |
666 | assert(privkey != NULL((void *)0)); |
667 | #endif |
668 | switch (msg->sm_user->uu_priv) { |
669 | case PRIV_DES: |
670 | /* RFC3414, chap 8.1.1.1. */ |
671 | for (i = 0; i < 8; i++) |
672 | iv[i] = msg->sm_salt[i] ^ privkey[SNMP_USM_SALTLEN8 + i]; |
673 | break; |
674 | case PRIV_AES: |
675 | /* RFC3826, chap 3.1.2.1. */ |
676 | 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)); |
677 | memcpy(iv, &ivv, sizeof(ivv)); |
678 | 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)); |
679 | memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv)); |
680 | memcpy(iv + 2 * sizeof(ivv), msg->sm_salt, SNMP_USM_SALTLEN8); |
681 | break; |
682 | default: |
683 | return -1; |
684 | } |
685 | |
686 | if ((ctx = EVP_CIPHER_CTX_new()) == NULL((void *)0)) |
687 | return -1; |
688 | |
689 | if (!EVP_CipherInit(ctx, cipher, privkey, iv, do_encrypt)) { |
690 | EVP_CIPHER_CTX_free(ctx); |
691 | return -1; |
692 | } |
693 | |
694 | if (!do_encrypt) |
695 | EVP_CIPHER_CTX_set_padding(ctx, 0); |
696 | |
697 | if (EVP_CipherUpdate(ctx, outbuf, &len, inbuf, inlen) && |
698 | EVP_CipherFinal_ex(ctx, outbuf + len, &len2)) |
699 | rv = len + len2; |
700 | else |
701 | rv = -1; |
702 | |
703 | EVP_CIPHER_CTX_free(ctx); |
704 | return rv; |
705 | } |
706 | |
707 | /* |
708 | * RFC3414, Password to Key Algorithm |
709 | */ |
710 | char * |
711 | usm_passwd2key(const EVP_MD *md, char *passwd, int *maxlen) |
712 | { |
713 | EVP_MD_CTX *ctx; |
714 | int i, count; |
715 | u_char *pw, *c; |
716 | u_char pwbuf[2 * EVP_MAX_MD_SIZE64 + SNMPD_MAXENGINEIDLEN32]; |
717 | u_char keybuf[EVP_MAX_MD_SIZE64]; |
718 | unsigned dlen; |
719 | char *key; |
720 | |
721 | if ((ctx = EVP_MD_CTX_new()) == NULL((void *)0)) |
722 | return NULL((void *)0); |
723 | if (!EVP_DigestInit_ex(ctx, md, NULL((void *)0))) { |
724 | EVP_MD_CTX_free(ctx); |
725 | return NULL((void *)0); |
726 | } |
727 | pw = (u_char *)passwd; |
728 | for (count = 0; count < 1048576; count += 64) { |
729 | c = pwbuf; |
730 | for (i = 0; i < 64; i++) { |
731 | if (*pw == '\0') |
732 | pw = (u_char *)passwd; |
733 | *c++ = *pw++; |
734 | } |
735 | if (!EVP_DigestUpdate(ctx, pwbuf, 64)) { |
736 | EVP_MD_CTX_free(ctx); |
737 | return NULL((void *)0); |
738 | } |
739 | } |
740 | if (!EVP_DigestFinal_ex(ctx, keybuf, &dlen)) { |
741 | EVP_MD_CTX_free(ctx); |
742 | return NULL((void *)0); |
743 | } |
744 | |
745 | /* Localize the key */ |
746 | #ifdef DEBUG |
747 | assert(snmpd_env->sc_engineid_len <= SNMPD_MAXENGINEIDLEN32); |
748 | #endif |
749 | memcpy(pwbuf, keybuf, dlen); |
750 | memcpy(pwbuf + dlen, snmpd_env->sc_engineid, |
751 | snmpd_env->sc_engineid_len); |
752 | memcpy(pwbuf + dlen + snmpd_env->sc_engineid_len, keybuf, dlen); |
753 | |
754 | if (!EVP_DigestInit_ex(ctx, md, NULL((void *)0)) || |
755 | !EVP_DigestUpdate(ctx, pwbuf, |
756 | 2 * dlen + snmpd_env->sc_engineid_len) || |
757 | !EVP_DigestFinal_ex(ctx, keybuf, &dlen)) { |
758 | EVP_MD_CTX_free(ctx); |
759 | return NULL((void *)0); |
760 | } |
761 | EVP_MD_CTX_free(ctx); |
762 | |
763 | if (*maxlen > 0 && dlen > (unsigned)*maxlen) |
764 | dlen = (unsigned)*maxlen; |
765 | if ((key = malloc(dlen)) == NULL((void *)0)) |
766 | fatal("key"); |
767 | memcpy(key, keybuf, dlen); |
768 | *maxlen = (int)dlen; |
769 | return key; |
770 | } |