1 /* $OpenBSD: usm.c,v 1.9 2015/01/16 00:05:13 deraadt 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.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 extern struct snmpd *env; 46 47 SLIST_HEAD(, usmuser) usmuserlist; 48 49 const EVP_MD *usm_get_md(enum usmauth); 50 const EVP_CIPHER *usm_get_cipher(enum usmpriv); 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) { 69 if ((md = usm_get_md(up->uu_auth)) == NULL) 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_KEYLEN; 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_NONE: 101 default: 102 return NULL; 103 } 104 } 105 106 const EVP_CIPHER * 107 usm_get_cipher(enum usmpriv up) 108 { 109 switch (up) { 110 case PRIV_DES: 111 return EVP_des_cbc(); 112 case PRIV_AES: 113 return EVP_aes_128_cfb128(); 114 case PRIV_NONE: 115 default: 116 return NULL; 117 } 118 } 119 120 struct usmuser * 121 usm_newuser(char *name, const char **errp) 122 { 123 struct usmuser *up = usm_finduser(name); 124 if (up != NULL) { 125 *errp = "user redefined"; 126 return NULL; 127 } 128 if ((up = calloc(1, sizeof(*up))) == NULL) 129 fatal("usm"); 130 up->uu_name = name; 131 SLIST_INSERT_HEAD(&usmuserlist, up, uu_next); 132 return up; 133 } 134 135 struct usmuser * 136 usm_finduser(char *name) 137 { 138 struct usmuser *up; 139 140 SLIST_FOREACH(up, &usmuserlist, uu_next) { 141 if (!strcmp(up->uu_name, name)) 142 return up; 143 } 144 return NULL; 145 } 146 147 int 148 usm_checkuser(struct usmuser *up, const char **errp) 149 { 150 char *auth = NULL, *priv = NULL; 151 152 if (up->uu_auth != AUTH_NONE && up->uu_authkey == NULL) { 153 *errp = "missing auth passphrase"; 154 goto fail; 155 } else if (up->uu_auth == AUTH_NONE && up->uu_authkey != NULL) 156 up->uu_auth = AUTH_DEFAULT; 157 158 if (up->uu_priv != PRIV_NONE && up->uu_privkey == NULL) { 159 *errp = "missing priv passphrase"; 160 goto fail; 161 } else if (up->uu_priv == PRIV_NONE && up->uu_privkey != NULL) 162 up->uu_priv = PRIV_DEFAULT; 163 164 if (up->uu_auth == AUTH_NONE && up->uu_priv != PRIV_NONE) { 165 /* Standard prohibits noAuthPriv */ 166 *errp = "auth is mandatory with enc"; 167 goto fail; 168 } 169 170 switch (up->uu_auth) { 171 case AUTH_NONE: 172 auth = "none"; 173 break; 174 case AUTH_MD5: 175 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 176 auth = "HMAC-MD5-96"; 177 break; 178 case AUTH_SHA1: 179 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 180 auth = "HMAC-SHA1-96"; 181 break; 182 } 183 184 switch (up->uu_priv) { 185 case PRIV_NONE: 186 priv = "none"; 187 break; 188 case PRIV_DES: 189 up->uu_seclevel |= SNMP_MSGFLAG_PRIV; 190 priv = "CBC-DES"; 191 break; 192 case PRIV_AES: 193 up->uu_seclevel |= SNMP_MSGFLAG_PRIV; 194 priv = "CFB128-AES-128"; 195 break; 196 } 197 198 log_debug("user \"%s\" auth %s enc %s", up->uu_name, auth, priv); 199 return 0; 200 201 fail: 202 free(up->uu_name); 203 free(up->uu_authkey); 204 free(up->uu_privkey); 205 SLIST_REMOVE(&usmuserlist, up, usmuser, uu_next); 206 free(up); 207 return -1; 208 } 209 210 struct ber_element * 211 usm_decode(struct snmp_message *msg, struct ber_element *elm, const char **errp) 212 { 213 struct snmp_stats *stats = &env->sc_stats; 214 off_t offs, offs2; 215 char *usmparams; 216 size_t len; 217 size_t enginelen, userlen, digestlen, saltlen; 218 struct ber ber; 219 struct ber_element *usm = NULL, *next = NULL, *decr; 220 char *engineid; 221 char *user; 222 char *digest, *salt; 223 u_long now; 224 long long engine_boots, engine_time; 225 226 bzero(&ber, sizeof(ber)); 227 offs = ber_getpos(elm); 228 229 if (ber_get_nstring(elm, (void *)&usmparams, &len) < 0) { 230 *errp = "cannot decode security params"; 231 goto done; 232 } 233 234 ber.fd = -1; 235 ber_set_readbuf(&ber, usmparams, len); 236 usm = ber_read_elements(&ber, NULL); 237 if (usm == NULL) { 238 *errp = "cannot decode security params"; 239 goto done; 240 } 241 242 #ifdef DEBUG 243 fprintf(stderr, "decode USM parameters:\n"); 244 smi_debug_elements(usm); 245 #endif 246 247 if (ber_scanf_elements(usm, "{xiixpxx", &engineid, &enginelen, 248 &engine_boots, &engine_time, &user, &userlen, &offs2, 249 &digest, &digestlen, &salt, &saltlen) != 0) { 250 *errp = "cannot decode USM params"; 251 goto done; 252 } 253 254 log_debug("USM: engineid '%s', engine boots %lld, engine time %lld, " 255 "user '%s'", tohexstr(engineid, enginelen), engine_boots, 256 engine_time, user); 257 258 if (enginelen > SNMPD_MAXENGINEIDLEN || 259 userlen > SNMPD_MAXUSERNAMELEN || 260 (digestlen != (MSG_HAS_AUTH(msg) ? SNMP_USM_DIGESTLEN : 0)) || 261 (saltlen != (MSG_HAS_PRIV(msg) ? SNMP_USM_SALTLEN : 0))) { 262 *errp = "bad field length"; 263 goto done; 264 } 265 266 if (enginelen != env->sc_engineid_len || 267 memcmp(engineid, env->sc_engineid, enginelen) != 0) { 268 *errp = "unknown engine id"; 269 msg->sm_usmerr = OIDVAL_usmErrEngineId; 270 stats->snmp_usmnosuchengine++; 271 goto done; 272 } 273 274 if (engine_boots != 0LL && engine_time != 0LL) { 275 now = snmpd_engine_time(); 276 if (engine_boots != env->sc_engine_boots || 277 engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW) || 278 engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW)) { 279 *errp = "out of time window"; 280 msg->sm_usmerr = OIDVAL_usmErrTimeWindow; 281 stats->snmp_usmtimewindow++; 282 goto done; 283 } 284 } 285 286 msg->sm_engine_boots = (u_int32_t)engine_boots; 287 msg->sm_engine_time = (u_int32_t)engine_time; 288 289 memcpy(msg->sm_username, user, userlen); 290 msg->sm_username[userlen] = '\0'; 291 msg->sm_user = usm_finduser(msg->sm_username); 292 if (msg->sm_user == NULL) { 293 *errp = "no such user"; 294 msg->sm_usmerr = OIDVAL_usmErrUserName; 295 stats->snmp_usmnosuchuser++; 296 goto done; 297 } 298 if (MSG_SECLEVEL(msg) > msg->sm_user->uu_seclevel) { 299 *errp = "unsupported security model"; 300 msg->sm_usmerr = OIDVAL_usmErrSecLevel; 301 stats->snmp_usmbadseclevel++; 302 goto done; 303 } 304 305 /* 306 * offs is the offset of the USM string within the serialized msg 307 * and offs2 the offset of the digest within the USM string. 308 */ 309 if (!usm_valid_digest(msg, offs + offs2, digest, digestlen)) { 310 *errp = "bad msg digest"; 311 msg->sm_usmerr = OIDVAL_usmErrDigest; 312 stats->snmp_usmwrongdigest++; 313 goto done; 314 } 315 316 if (MSG_HAS_PRIV(msg)) { 317 memcpy(msg->sm_salt, salt, saltlen); 318 if ((decr = usm_decrypt(msg, elm->be_next)) == NULL) { 319 *errp = "cannot decrypt msg"; 320 msg->sm_usmerr = OIDVAL_usmErrDecrypt; 321 stats->snmp_usmdecrypterr++; 322 goto done; 323 } 324 ber_replace_elements(elm, decr); 325 } 326 next = elm->be_next; 327 328 done: 329 ber_free(&ber); 330 if (usm != NULL) 331 ber_free_elements(usm); 332 return next; 333 } 334 335 struct ber_element * 336 usm_encode(struct snmp_message *msg, struct ber_element *e) 337 { 338 struct ber ber; 339 struct ber_element *usm, *a, *res = NULL; 340 void *ptr; 341 char digest[SNMP_USM_DIGESTLEN]; 342 size_t digestlen, saltlen, len; 343 344 msg->sm_digest_offs = 0; 345 bzero(&ber, sizeof(ber)); 346 ber.fd = -1; 347 348 usm = ber_add_sequence(NULL); 349 350 if (MSG_HAS_AUTH(msg)) { 351 /* 352 * Fill in enough zeroes and remember the position within the 353 * messages. The digest will be calculated once the message 354 * is complete. 355 */ 356 #ifdef DEBUG 357 assert(msg->sm_user != NULL); 358 #endif 359 bzero(digest, sizeof(digest)); 360 digestlen = sizeof(digest); 361 } else 362 digestlen = 0; 363 364 if (MSG_HAS_PRIV(msg)) { 365 #ifdef DEBUG 366 assert(msg->sm_user != NULL); 367 #endif 368 ++(msg->sm_user->uu_salt); 369 memcpy(msg->sm_salt, &msg->sm_user->uu_salt, 370 sizeof(msg->sm_salt)); 371 saltlen = sizeof(msg->sm_salt); 372 } else 373 saltlen = 0; 374 375 msg->sm_engine_boots = (u_int32_t)env->sc_engine_boots; 376 msg->sm_engine_time = (u_int32_t)snmpd_engine_time(); 377 if ((a = ber_printf_elements(usm, "xdds", 378 env->sc_engineid, env->sc_engineid_len, msg->sm_engine_boots, 379 msg->sm_engine_time, msg->sm_username)) == NULL) 380 goto done; 381 382 if ((a = ber_add_nstring(a, digest, digestlen)) == NULL) 383 goto done; 384 if (digestlen > 0) 385 ber_set_writecallback(a, usm_cb_digest, msg); 386 387 if ((a = ber_add_nstring(a, msg->sm_salt, saltlen)) == NULL) 388 goto done; 389 390 #ifdef DEBUG 391 fprintf(stderr, "encode USM parameters:\n"); 392 smi_debug_elements(usm); 393 #endif 394 len = ber_write_elements(&ber, usm); 395 if (ber_get_writebuf(&ber, &ptr) > 0) { 396 res = ber_add_nstring(e, (char *)ptr, len); 397 if (digestlen > 0) 398 ber_set_writecallback(res, usm_cb_digest, msg); 399 } 400 401 done: 402 ber_free(&ber); 403 ber_free_elements(usm); 404 return res; 405 } 406 407 void 408 usm_cb_digest(void *arg, size_t offs) 409 { 410 struct snmp_message *msg = arg; 411 msg->sm_digest_offs += offs; 412 } 413 414 struct ber_element * 415 usm_encrypt(struct snmp_message *msg, struct ber_element *pdu) 416 { 417 struct ber ber; 418 struct ber_element *encrpdu = NULL; 419 void *ptr; 420 int len; 421 ssize_t elen; 422 u_char encbuf[READ_BUF_SIZE]; 423 424 if (!MSG_HAS_PRIV(msg)) 425 return pdu; 426 427 bzero(&ber, sizeof(ber)); 428 ber.fd = -1; 429 430 #ifdef DEBUG 431 fprintf(stderr, "encrypted PDU:\n"); 432 smi_debug_elements(pdu); 433 #endif 434 435 len = ber_write_elements(&ber, pdu); 436 if (ber_get_writebuf(&ber, &ptr) > 0) { 437 elen = usm_crypt(msg, ptr, len, encbuf, 1); 438 if (elen > 0) 439 encrpdu = ber_add_nstring(NULL, (char *)encbuf, elen); 440 } 441 442 ber_free(&ber); 443 ber_free_elements(pdu); 444 return encrpdu; 445 } 446 447 /* 448 * Calculate message digest and replace within message 449 */ 450 void 451 usm_finalize_digest(struct snmp_message *msg, char *buf, ssize_t len) 452 { 453 const EVP_MD *md; 454 u_char digest[EVP_MAX_MD_SIZE]; 455 unsigned hlen; 456 457 if (msg->sm_resp == NULL || 458 !MSG_HAS_AUTH(msg) || 459 msg->sm_user == NULL || 460 msg->sm_digest_offs == 0 || 461 len <= 0) 462 return; 463 bzero(digest, SNMP_USM_DIGESTLEN); 464 #ifdef DEBUG 465 assert(msg->sm_digest_offs + SNMP_USM_DIGESTLEN <= (size_t)len); 466 assert(!memcmp(buf + msg->sm_digest_offs, digest, SNMP_USM_DIGESTLEN)); 467 #endif 468 469 if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL) 470 return; 471 472 HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen, 473 (u_char*)buf, (size_t)len, digest, &hlen); 474 475 memcpy(buf + msg->sm_digest_offs, digest, SNMP_USM_DIGESTLEN); 476 return; 477 } 478 479 void 480 usm_make_report(struct snmp_message *msg) 481 { 482 struct ber_oid usmstat = OID(MIB_usmStats, 0, 0); 483 484 /* Always send report in clear-text */ 485 msg->sm_flags = 0; 486 msg->sm_context = SNMP_C_REPORT; 487 msg->sm_username[0] = '\0'; 488 usmstat.bo_id[OIDIDX_usmStats] = msg->sm_usmerr; 489 usmstat.bo_n = OIDIDX_usmStats + 2; 490 if (msg->sm_varbindresp != NULL) 491 ber_free_elements(msg->sm_varbindresp); 492 msg->sm_varbindresp = ber_add_sequence(NULL); 493 mps_getreq(NULL, msg->sm_varbindresp, &usmstat, msg->sm_version); 494 return; 495 } 496 497 int 498 usm_valid_digest(struct snmp_message *msg, off_t offs, 499 char *digest, size_t digestlen) 500 { 501 const EVP_MD *md; 502 u_char exp_digest[EVP_MAX_MD_SIZE]; 503 unsigned hlen; 504 505 if (!MSG_HAS_AUTH(msg)) 506 return 1; 507 508 if (digestlen != SNMP_USM_DIGESTLEN) 509 return 0; 510 511 #ifdef DEBUG 512 assert(offs + digestlen <= msg->sm_datalen); 513 assert(bcmp(&msg->sm_data[offs], digest, digestlen) == 0); 514 #endif 515 516 if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL) 517 return 0; 518 519 memset(&msg->sm_data[offs], 0, digestlen); 520 HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen, 521 msg->sm_data, msg->sm_datalen, exp_digest, &hlen); 522 /* we don't bother to restore the original message */ 523 524 if (hlen < digestlen) 525 return 0; 526 527 return memcmp(digest, exp_digest, digestlen) == 0; 528 } 529 530 struct ber_element * 531 usm_decrypt(struct snmp_message *msg, struct ber_element *encr) 532 { 533 u_char *privstr; 534 size_t privlen; 535 u_char buf[READ_BUF_SIZE]; 536 struct ber ber; 537 struct ber_element *scoped_pdu = NULL; 538 ssize_t scoped_pdu_len; 539 540 if (ber_get_nstring(encr, (void *)&privstr, &privlen) < 0) 541 return NULL; 542 543 scoped_pdu_len = usm_crypt(msg, privstr, (int)privlen, buf, 0); 544 if (scoped_pdu_len < 0) 545 return NULL; 546 547 bzero(&ber, sizeof(ber)); 548 ber.fd = -1; 549 ber_set_readbuf(&ber, buf, scoped_pdu_len); 550 scoped_pdu = ber_read_elements(&ber, NULL); 551 552 #ifdef DEBUG 553 if (scoped_pdu != NULL) { 554 fprintf(stderr, "decrypted scoped PDU:\n"); 555 smi_debug_elements(scoped_pdu); 556 } 557 #endif 558 559 ber_free(&ber); 560 return scoped_pdu; 561 } 562 563 ssize_t 564 usm_crypt(struct snmp_message *msg, u_char *inbuf, int inlen, u_char *outbuf, 565 int do_encrypt) 566 { 567 const EVP_CIPHER *cipher; 568 EVP_CIPHER_CTX ctx; 569 u_char *privkey; 570 int i; 571 u_char iv[EVP_MAX_IV_LENGTH]; 572 int len, len2; 573 int rv; 574 u_int32_t ivv; 575 576 if ((cipher = usm_get_cipher(msg->sm_user->uu_priv)) == NULL) 577 return -1; 578 579 privkey = (u_char *)msg->sm_user->uu_privkey; 580 #ifdef DEBUG 581 assert(privkey != NULL); 582 #endif 583 switch (msg->sm_user->uu_priv) { 584 case PRIV_DES: 585 /* RFC3414, chap 8.1.1.1. */ 586 for (i = 0; i < 8; i++) 587 iv[i] = msg->sm_salt[i] ^ privkey[SNMP_USM_SALTLEN + i]; 588 break; 589 case PRIV_AES: 590 /* RFC3826, chap 3.1.2.1. */ 591 ivv = htobe32(msg->sm_engine_boots); 592 memcpy(iv, &ivv, sizeof(ivv)); 593 ivv = htobe32(msg->sm_engine_time); 594 memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv)); 595 memcpy(iv + 2 * sizeof(ivv), msg->sm_salt, SNMP_USM_SALTLEN); 596 break; 597 default: 598 return -1; 599 } 600 601 if (!EVP_CipherInit(&ctx, cipher, privkey, iv, do_encrypt)) 602 return -1; 603 604 if (!do_encrypt) 605 EVP_CIPHER_CTX_set_padding(&ctx, 0); 606 607 if (EVP_CipherUpdate(&ctx, outbuf, &len, inbuf, inlen) && 608 EVP_CipherFinal(&ctx, outbuf + len, &len2)) 609 rv = len + len2; 610 else 611 rv = -1; 612 613 EVP_CIPHER_CTX_cleanup(&ctx); 614 return rv; 615 } 616 617 /* 618 * RFC3414, Password to Key Algorithm 619 */ 620 char * 621 usm_passwd2key(const EVP_MD *md, char *passwd, int *maxlen) 622 { 623 EVP_MD_CTX ctx; 624 int i, count; 625 u_char *pw, *c; 626 u_char pwbuf[2 * EVP_MAX_MD_SIZE + SNMPD_MAXENGINEIDLEN]; 627 u_char keybuf[EVP_MAX_MD_SIZE]; 628 unsigned dlen; 629 char *key; 630 631 EVP_DigestInit(&ctx, md); 632 pw = (u_char *)passwd; 633 for (count = 0; count < 1048576; count += 64) { 634 c = pwbuf; 635 for (i = 0; i < 64; i++) { 636 if (*pw == '\0') 637 pw = (u_char *)passwd; 638 *c++ = *pw++; 639 } 640 EVP_DigestUpdate(&ctx, pwbuf, 64); 641 } 642 EVP_DigestFinal(&ctx, keybuf, &dlen); 643 EVP_MD_CTX_cleanup(&ctx); 644 645 /* Localize the key */ 646 #ifdef DEBUG 647 assert(env->sc_engineid_len <= SNMPD_MAXENGINEIDLEN); 648 #endif 649 memcpy(pwbuf, keybuf, dlen); 650 memcpy(pwbuf + dlen, env->sc_engineid, env->sc_engineid_len); 651 memcpy(pwbuf + dlen + env->sc_engineid_len, keybuf, dlen); 652 653 EVP_DigestInit(&ctx, md); 654 EVP_DigestUpdate(&ctx, pwbuf, 2 * dlen + env->sc_engineid_len); 655 EVP_DigestFinal(&ctx, keybuf, &dlen); 656 EVP_MD_CTX_cleanup(&ctx); 657 658 if (*maxlen > 0 && dlen > (unsigned)*maxlen) 659 dlen = (unsigned)*maxlen; 660 if ((key = malloc(dlen)) == NULL) 661 fatal("key"); 662 memcpy(key, keybuf, dlen); 663 *maxlen = (int)dlen; 664 return key; 665 } 666