snmpcrypto.c revision 267654
1/*- 2 * Copyright (c) 2010 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Shteryana Sotirova Shopova under 6 * sponsorship from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: releng/9.3/contrib/bsnmp/lib/snmpcrypto.c 216482 2010-12-16 11:20:37Z syrinx $ 30 */ 31#include <sys/types.h> 32#include <sys/socket.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <stddef.h> 36#include <stdarg.h> 37#ifdef HAVE_STDINT_H 38#include <stdint.h> 39#elif defined(HAVE_INTTYPES_H) 40#include <inttypes.h> 41#endif 42#include <string.h> 43#include <ctype.h> 44#include <errno.h> 45#include <netinet/in.h> 46 47#ifdef HAVE_LIBCRYPTO 48#include <openssl/evp.h> 49#endif 50 51#include "asn1.h" 52#include "snmp.h" 53#include "snmppriv.h" 54 55#define SNMP_PRIV_AES_IV_SIZ 16 56#define SNMP_EXTENDED_KEY_SIZ 64 57#define SNMP_AUTH_KEY_LOOPCNT 1048576 58#define SNMP_AUTH_BUF_SIZE 72 59 60static const uint8_t ipad = 0x36; 61static const uint8_t opad = 0x5c; 62 63#ifdef HAVE_LIBCRYPTO 64 65static int32_t 66snmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx, 67 const EVP_MD **dtype, uint32_t *keylen) 68{ 69 if (user->auth_proto == SNMP_AUTH_HMAC_MD5) { 70 *dtype = EVP_md5(); 71 *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ; 72 } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) { 73 *dtype = EVP_sha1(); 74 *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ; 75 } else if (user->auth_proto == SNMP_AUTH_NOAUTH) 76 return (0); 77 else { 78 snmp_error("unknown authentication option - %d", 79 user->auth_proto); 80 return (-1); 81 } 82 83 if (EVP_DigestInit(ctx, *dtype) != 1) 84 return (-1); 85 86 return (1); 87} 88 89enum snmp_code 90snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest) 91{ 92 uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ]; 93 uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ]; 94 uint32_t i, keylen, olen; 95 int32_t err; 96 const EVP_MD *dtype; 97 EVP_MD_CTX ctx; 98 99 err = snmp_digest_init(&pdu->user, &ctx, &dtype, &keylen); 100 if (err < 0) 101 return (SNMP_CODE_BADDIGEST); 102 else if (err == 0) 103 return (SNMP_CODE_OK); 104 105 memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest)); 106 memcpy(extkey, pdu->user.auth_key, keylen); 107 memset(extkey + keylen, 0, sizeof(extkey) - keylen); 108 109 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) { 110 key1[i] = extkey[i] ^ ipad; 111 key2[i] = extkey[i] ^ opad; 112 } 113 114 if (EVP_DigestUpdate(&ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 || 115 EVP_DigestUpdate(&ctx, pdu->outer_ptr, pdu->outer_len) != 1 || 116 EVP_DigestFinal(&ctx, md, &olen) != 1) 117 goto failed; 118 119 if (EVP_DigestInit(&ctx, dtype) != 1 || 120 EVP_DigestUpdate(&ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 || 121 EVP_DigestUpdate(&ctx, md, olen) != 1 || 122 EVP_DigestFinal(&ctx, md, &olen) != 1) 123 goto failed; 124 125 if (olen < SNMP_USM_AUTH_SIZE) { 126 snmp_error("bad digest size - %d", olen); 127 EVP_MD_CTX_cleanup(&ctx); 128 return (SNMP_CODE_BADDIGEST); 129 } 130 131 memcpy(digest, md, SNMP_USM_AUTH_SIZE); 132 EVP_MD_CTX_cleanup(&ctx); 133 return (SNMP_CODE_OK); 134 135failed: 136 EVP_MD_CTX_cleanup(&ctx); 137 return (SNMP_CODE_BADDIGEST); 138} 139 140static int32_t 141snmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len, 142 const EVP_CIPHER **ctype, uint8_t *piv) 143{ 144 int i; 145 uint32_t netint; 146 147 if (pdu->user.priv_proto == SNMP_PRIV_DES) { 148 if (len % 8 != 0) 149 return (-1); 150 *ctype = EVP_des_cbc(); 151 memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); 152 for (i = 0; i < 8; i++) 153 piv[i] = piv[i] ^ pdu->user.priv_key[8 + i]; 154 } else if (pdu->user.priv_proto == SNMP_PRIV_AES) { 155 *ctype = EVP_aes_128_cfb128(); 156 netint = htonl(pdu->engine.engine_boots); 157 memcpy(piv, &netint, sizeof(netint)); 158 piv += sizeof(netint); 159 netint = htonl(pdu->engine.engine_time); 160 memcpy(piv, &netint, sizeof(netint)); 161 piv += sizeof(netint); 162 memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt)); 163 } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV) 164 return (0); 165 else { 166 snmp_error("unknown privacy option - %d", pdu->user.priv_proto); 167 return (-1); 168 } 169 170 return (1); 171} 172 173enum snmp_code 174snmp_pdu_encrypt(const struct snmp_pdu *pdu) 175{ 176 int32_t err, olen; 177 uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; 178 const EVP_CIPHER *ctype; 179 EVP_CIPHER_CTX ctx; 180 181 err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv); 182 if (err < 0) 183 return (SNMP_CODE_EDECRYPT); 184 else if (err == 0) 185 return (SNMP_CODE_OK); 186 187 if (EVP_EncryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1) 188 return (SNMP_CODE_FAILED); 189 190 if (EVP_EncryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, 191 pdu->scoped_len) != 1 || 192 EVP_EncryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { 193 EVP_CIPHER_CTX_cleanup(&ctx); 194 return (SNMP_CODE_FAILED); 195 } 196 197 EVP_CIPHER_CTX_cleanup(&ctx); 198 return (SNMP_CODE_OK); 199} 200 201enum snmp_code 202snmp_pdu_decrypt(const struct snmp_pdu *pdu) 203{ 204 int32_t err, olen; 205 uint8_t iv[SNMP_PRIV_AES_IV_SIZ]; 206 const EVP_CIPHER *ctype; 207 EVP_CIPHER_CTX ctx; 208 209 err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv); 210 if (err < 0) 211 return (SNMP_CODE_EDECRYPT); 212 else if (err == 0) 213 return (SNMP_CODE_OK); 214 215 if (EVP_DecryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1 || 216 EVP_CIPHER_CTX_set_padding(&ctx, 0) != 1) 217 return (SNMP_CODE_EDECRYPT); 218 219 if (EVP_DecryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr, 220 pdu->scoped_len) != 1 || 221 EVP_DecryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) { 222 EVP_CIPHER_CTX_cleanup(&ctx); 223 return (SNMP_CODE_EDECRYPT); 224 } 225 226 EVP_CIPHER_CTX_cleanup(&ctx); 227 return (SNMP_CODE_OK); 228} 229 230/* [RFC 3414] - A.2. Password to Key Algorithm */ 231enum snmp_code 232snmp_passwd_to_keys(struct snmp_user *user, char *passwd) 233{ 234 int err, loop, i, pwdlen; 235 uint32_t keylen, olen; 236 const EVP_MD *dtype; 237 EVP_MD_CTX ctx; 238 uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; 239 240 if (passwd == NULL || user == NULL) 241 return (SNMP_CODE_FAILED); 242 243 err = snmp_digest_init(user, &ctx, &dtype, &keylen); 244 if (err < 0) 245 return (SNMP_CODE_BADDIGEST); 246 else if (err == 0) 247 return (SNMP_CODE_OK); 248 249 memset(user->auth_key, 0, sizeof(user->auth_key)); 250 pwdlen = strlen(passwd); 251 252 for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) { 253 for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) 254 authbuf[i] = passwd[(loop + i) % pwdlen]; 255 if (EVP_DigestUpdate(&ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1) 256 goto failed; 257 } 258 259 if (EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) 260 goto failed; 261 262 EVP_MD_CTX_cleanup(&ctx); 263 return (SNMP_CODE_OK); 264 265failed: 266 EVP_MD_CTX_cleanup(&ctx); 267 return (SNMP_CODE_BADDIGEST); 268} 269 270/* [RFC 3414] - 2.6. Key Localization Algorithm */ 271enum snmp_code 272snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen) 273{ 274 int err; 275 uint32_t keylen, olen; 276 const EVP_MD *dtype; 277 EVP_MD_CTX ctx; 278 uint8_t authbuf[SNMP_AUTH_BUF_SIZE]; 279 280 if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ) 281 return (SNMP_CODE_FAILED); 282 283 memset(user->priv_key, 0, sizeof(user->priv_key)); 284 memset(authbuf, 0, sizeof(authbuf)); 285 286 err = snmp_digest_init(user, &ctx, &dtype, &keylen); 287 if (err < 0) 288 return (SNMP_CODE_BADDIGEST); 289 else if (err == 0) 290 return (SNMP_CODE_OK); 291 292 memcpy(authbuf, user->auth_key, keylen); 293 memcpy(authbuf + keylen, eid, elen); 294 memcpy(authbuf + keylen + elen, user->auth_key, keylen); 295 296 if (EVP_DigestUpdate(&ctx, authbuf, 2 * keylen + elen) != 1 || 297 EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) { 298 EVP_MD_CTX_cleanup(&ctx); 299 return (SNMP_CODE_BADDIGEST); 300 } 301 EVP_MD_CTX_cleanup(&ctx); 302 303 if (user->priv_proto != SNMP_PRIV_NOPRIV) 304 memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key)); 305 306 return (SNMP_CODE_OK); 307} 308 309enum snmp_code 310snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange) 311{ 312 int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4]; 313 uint32_t i, keylen, olen; 314 const EVP_MD *dtype; 315 EVP_MD_CTX ctx; 316 317 err = snmp_digest_init(user, &ctx, &dtype, &keylen); 318 if (err < 0) 319 return (SNMP_CODE_BADDIGEST); 320 else if (err == 0) 321 return (SNMP_CODE_OK); 322 323 for (i = 0; i < keylen / 4; i++) 324 rvalue[i] = random(); 325 326 memcpy(keychange, user->auth_key, keylen); 327 memcpy(keychange + keylen, rvalue, keylen); 328 329 if (EVP_DigestUpdate(&ctx, keychange, 2 * keylen) != 1 || 330 EVP_DigestFinal(&ctx, keychange, &olen) != 1) { 331 EVP_MD_CTX_cleanup(&ctx); 332 return (SNMP_CODE_BADDIGEST); 333 } 334 335 EVP_MD_CTX_cleanup(&ctx); 336 return (SNMP_CODE_OK); 337} 338 339#else /* !HAVE_LIBCRYPTO */ 340 341enum snmp_code 342snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused) 343{ 344 if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH) 345 return (SNMP_CODE_BADSECLEVEL); 346 347 348 return (SNMP_CODE_OK); 349} 350 351enum snmp_code 352snmp_pdu_encrypt(const struct snmp_pdu *pdu) 353{ 354 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) 355 return (SNMP_CODE_BADSECLEVEL); 356 357 return (SNMP_CODE_OK); 358} 359 360enum snmp_code 361snmp_pdu_decrypt(const struct snmp_pdu *pdu) 362{ 363 if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV) 364 return (SNMP_CODE_BADSECLEVEL); 365 366 return (SNMP_CODE_OK); 367} 368 369int 370snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused) 371{ 372 if (user->auth_proto == SNMP_AUTH_NOAUTH && 373 user->priv_proto == SNMP_PRIV_NOPRIV) 374 return (SNMP_CODE_OK); 375 376 errno = EPROTONOSUPPORT; 377 378 return (SNMP_CODE_FAILED); 379} 380 381int 382snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused, 383 uint32_t elen __unused) 384{ 385 if (user->auth_proto == SNMP_AUTH_NOAUTH && 386 user->priv_proto == SNMP_PRIV_NOPRIV) 387 return (SNMP_CODE_OK); 388 389 errno = EPROTONOSUPPORT; 390 391 return (SNMP_CODE_FAILED); 392} 393 394enum snmp_code 395snmp_calc_keychange(struct snmp_user *user __unused, 396 uint8_t *keychange __unused) 397{ 398 errno = EPROTONOSUPPORT; 399 return (SNMP_CODE_FAILED); 400} 401 402#endif /* HAVE_LIBCRYPTO */ 403