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