/contrib/bsnmp/lib/snmpcrypto.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 402 lines · 302 code · 68 blank · 32 comment · 105 complexity · 61476bfc45604f5fb3b0a8b835ee33f4 MD5 · raw file

  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$
  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. #ifdef HAVE_LIBCRYPTO
  47. #include <openssl/evp.h>
  48. #endif
  49. #include "asn1.h"
  50. #include "snmp.h"
  51. #include "snmppriv.h"
  52. #define SNMP_PRIV_AES_IV_SIZ 16
  53. #define SNMP_EXTENDED_KEY_SIZ 64
  54. #define SNMP_AUTH_KEY_LOOPCNT 1048576
  55. #define SNMP_AUTH_BUF_SIZE 72
  56. static const uint8_t ipad = 0x36;
  57. static const uint8_t opad = 0x5c;
  58. #ifdef HAVE_LIBCRYPTO
  59. static int32_t
  60. snmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx,
  61. const EVP_MD **dtype, uint32_t *keylen)
  62. {
  63. if (user->auth_proto == SNMP_AUTH_HMAC_MD5) {
  64. *dtype = EVP_md5();
  65. *keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
  66. } else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) {
  67. *dtype = EVP_sha1();
  68. *keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
  69. } else if (user->auth_proto == SNMP_AUTH_NOAUTH)
  70. return (0);
  71. else {
  72. snmp_error("unknown authentication option - %d",
  73. user->auth_proto);
  74. return (-1);
  75. }
  76. if (EVP_DigestInit(ctx, *dtype) != 1)
  77. return (-1);
  78. return (1);
  79. }
  80. enum snmp_code
  81. snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest)
  82. {
  83. uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ];
  84. uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ];
  85. uint32_t i, keylen, olen;
  86. int32_t err;
  87. const EVP_MD *dtype;
  88. EVP_MD_CTX ctx;
  89. err = snmp_digest_init(&pdu->user, &ctx, &dtype, &keylen);
  90. if (err < 0)
  91. return (SNMP_CODE_BADDIGEST);
  92. else if (err == 0)
  93. return (SNMP_CODE_OK);
  94. memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest));
  95. memcpy(extkey, pdu->user.auth_key, keylen);
  96. memset(extkey + keylen, 0, sizeof(extkey) - keylen);
  97. for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) {
  98. key1[i] = extkey[i] ^ ipad;
  99. key2[i] = extkey[i] ^ opad;
  100. }
  101. if (EVP_DigestUpdate(&ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 ||
  102. EVP_DigestUpdate(&ctx, pdu->outer_ptr, pdu->outer_len) != 1 ||
  103. EVP_DigestFinal(&ctx, md, &olen) != 1)
  104. goto failed;
  105. if (EVP_DigestInit(&ctx, dtype) != 1 ||
  106. EVP_DigestUpdate(&ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 ||
  107. EVP_DigestUpdate(&ctx, md, olen) != 1 ||
  108. EVP_DigestFinal(&ctx, md, &olen) != 1)
  109. goto failed;
  110. if (olen < SNMP_USM_AUTH_SIZE) {
  111. snmp_error("bad digest size - %d", olen);
  112. EVP_MD_CTX_cleanup(&ctx);
  113. return (SNMP_CODE_BADDIGEST);
  114. }
  115. memcpy(digest, md, SNMP_USM_AUTH_SIZE);
  116. EVP_MD_CTX_cleanup(&ctx);
  117. return (SNMP_CODE_OK);
  118. failed:
  119. EVP_MD_CTX_cleanup(&ctx);
  120. return (SNMP_CODE_BADDIGEST);
  121. }
  122. static int32_t
  123. snmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len,
  124. const EVP_CIPHER **ctype, uint8_t *piv)
  125. {
  126. int i;
  127. uint32_t netint;
  128. if (pdu->user.priv_proto == SNMP_PRIV_DES) {
  129. if (len % 8 != 0)
  130. return (-1);
  131. *ctype = EVP_des_cbc();
  132. memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
  133. for (i = 0; i < 8; i++)
  134. piv[i] = piv[i] ^ pdu->user.priv_key[8 + i];
  135. } else if (pdu->user.priv_proto == SNMP_PRIV_AES) {
  136. *ctype = EVP_aes_128_cfb128();
  137. netint = htonl(pdu->engine.engine_boots);
  138. memcpy(piv, &netint, sizeof(netint));
  139. piv += sizeof(netint);
  140. netint = htonl(pdu->engine.engine_time);
  141. memcpy(piv, &netint, sizeof(netint));
  142. piv += sizeof(netint);
  143. memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
  144. } else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV)
  145. return (0);
  146. else {
  147. snmp_error("unknown privacy option - %d", pdu->user.priv_proto);
  148. return (-1);
  149. }
  150. return (1);
  151. }
  152. enum snmp_code
  153. snmp_pdu_encrypt(const struct snmp_pdu *pdu)
  154. {
  155. int32_t err, olen;
  156. uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
  157. const EVP_CIPHER *ctype;
  158. EVP_CIPHER_CTX ctx;
  159. err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
  160. if (err < 0)
  161. return (SNMP_CODE_EDECRYPT);
  162. else if (err == 0)
  163. return (SNMP_CODE_OK);
  164. if (EVP_EncryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1)
  165. return (SNMP_CODE_FAILED);
  166. if (EVP_EncryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
  167. pdu->scoped_len) != 1 ||
  168. EVP_EncryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) {
  169. EVP_CIPHER_CTX_cleanup(&ctx);
  170. return (SNMP_CODE_FAILED);
  171. }
  172. EVP_CIPHER_CTX_cleanup(&ctx);
  173. return (SNMP_CODE_OK);
  174. }
  175. enum snmp_code
  176. snmp_pdu_decrypt(const struct snmp_pdu *pdu)
  177. {
  178. int32_t err, olen;
  179. uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
  180. const EVP_CIPHER *ctype;
  181. EVP_CIPHER_CTX ctx;
  182. err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
  183. if (err < 0)
  184. return (SNMP_CODE_EDECRYPT);
  185. else if (err == 0)
  186. return (SNMP_CODE_OK);
  187. if (EVP_DecryptInit(&ctx, ctype, pdu->user.priv_key, iv) != 1 ||
  188. EVP_CIPHER_CTX_set_padding(&ctx, 0) != 1)
  189. return (SNMP_CODE_EDECRYPT);
  190. if (EVP_DecryptUpdate(&ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
  191. pdu->scoped_len) != 1 ||
  192. EVP_DecryptFinal(&ctx, pdu->scoped_ptr + olen, &olen) != 1) {
  193. EVP_CIPHER_CTX_cleanup(&ctx);
  194. return (SNMP_CODE_EDECRYPT);
  195. }
  196. EVP_CIPHER_CTX_cleanup(&ctx);
  197. return (SNMP_CODE_OK);
  198. }
  199. /* [RFC 3414] - A.2. Password to Key Algorithm */
  200. enum snmp_code
  201. snmp_passwd_to_keys(struct snmp_user *user, char *passwd)
  202. {
  203. int err, loop, i, pwdlen;
  204. uint32_t keylen, olen;
  205. const EVP_MD *dtype;
  206. EVP_MD_CTX ctx;
  207. uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
  208. if (passwd == NULL || user == NULL)
  209. return (SNMP_CODE_FAILED);
  210. err = snmp_digest_init(user, &ctx, &dtype, &keylen);
  211. if (err < 0)
  212. return (SNMP_CODE_BADDIGEST);
  213. else if (err == 0)
  214. return (SNMP_CODE_OK);
  215. memset(user->auth_key, 0, sizeof(user->auth_key));
  216. pwdlen = strlen(passwd);
  217. for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) {
  218. for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++)
  219. authbuf[i] = passwd[(loop + i) % pwdlen];
  220. if (EVP_DigestUpdate(&ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1)
  221. goto failed;
  222. }
  223. if (EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1)
  224. goto failed;
  225. EVP_MD_CTX_cleanup(&ctx);
  226. return (SNMP_CODE_OK);
  227. failed:
  228. EVP_MD_CTX_cleanup(&ctx);
  229. return (SNMP_CODE_BADDIGEST);
  230. }
  231. /* [RFC 3414] - 2.6. Key Localization Algorithm */
  232. enum snmp_code
  233. snmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen)
  234. {
  235. int err;
  236. uint32_t keylen, olen;
  237. const EVP_MD *dtype;
  238. EVP_MD_CTX ctx;
  239. uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
  240. if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ)
  241. return (SNMP_CODE_FAILED);
  242. memset(user->priv_key, 0, sizeof(user->priv_key));
  243. memset(authbuf, 0, sizeof(authbuf));
  244. err = snmp_digest_init(user, &ctx, &dtype, &keylen);
  245. if (err < 0)
  246. return (SNMP_CODE_BADDIGEST);
  247. else if (err == 0)
  248. return (SNMP_CODE_OK);
  249. memcpy(authbuf, user->auth_key, keylen);
  250. memcpy(authbuf + keylen, eid, elen);
  251. memcpy(authbuf + keylen + elen, user->auth_key, keylen);
  252. if (EVP_DigestUpdate(&ctx, authbuf, 2 * keylen + elen) != 1 ||
  253. EVP_DigestFinal(&ctx, user->auth_key, &olen) != 1) {
  254. EVP_MD_CTX_cleanup(&ctx);
  255. return (SNMP_CODE_BADDIGEST);
  256. }
  257. EVP_MD_CTX_cleanup(&ctx);
  258. if (user->priv_proto != SNMP_PRIV_NOPRIV)
  259. memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key));
  260. return (SNMP_CODE_OK);
  261. }
  262. enum snmp_code
  263. snmp_calc_keychange(struct snmp_user *user, uint8_t *keychange)
  264. {
  265. int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4];
  266. uint32_t i, keylen, olen;
  267. const EVP_MD *dtype;
  268. EVP_MD_CTX ctx;
  269. err = snmp_digest_init(user, &ctx, &dtype, &keylen);
  270. if (err < 0)
  271. return (SNMP_CODE_BADDIGEST);
  272. else if (err == 0)
  273. return (SNMP_CODE_OK);
  274. for (i = 0; i < keylen / 4; i++)
  275. rvalue[i] = random();
  276. memcpy(keychange, user->auth_key, keylen);
  277. memcpy(keychange + keylen, rvalue, keylen);
  278. if (EVP_DigestUpdate(&ctx, keychange, 2 * keylen) != 1 ||
  279. EVP_DigestFinal(&ctx, keychange, &olen) != 1) {
  280. EVP_MD_CTX_cleanup(&ctx);
  281. return (SNMP_CODE_BADDIGEST);
  282. }
  283. EVP_MD_CTX_cleanup(&ctx);
  284. return (SNMP_CODE_OK);
  285. }
  286. #else /* !HAVE_LIBCRYPTO */
  287. enum snmp_code
  288. snmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused)
  289. {
  290. if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
  291. return (SNMP_CODE_BADSECLEVEL);
  292. return (SNMP_CODE_OK);
  293. }
  294. enum snmp_code
  295. snmp_pdu_encrypt(const struct snmp_pdu *pdu)
  296. {
  297. if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
  298. return (SNMP_CODE_BADSECLEVEL);
  299. return (SNMP_CODE_OK);
  300. }
  301. enum snmp_code
  302. snmp_pdu_decrypt(const struct snmp_pdu *pdu)
  303. {
  304. if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
  305. return (SNMP_CODE_BADSECLEVEL);
  306. return (SNMP_CODE_OK);
  307. }
  308. int
  309. snmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused)
  310. {
  311. if (user->auth_proto == SNMP_AUTH_NOAUTH &&
  312. user->priv_proto == SNMP_PRIV_NOPRIV)
  313. return (SNMP_CODE_OK);
  314. errno = EPROTONOSUPPORT;
  315. return (SNMP_CODE_FAILED);
  316. }
  317. int
  318. snmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused,
  319. uint32_t elen __unused)
  320. {
  321. if (user->auth_proto == SNMP_AUTH_NOAUTH &&
  322. user->priv_proto == SNMP_PRIV_NOPRIV)
  323. return (SNMP_CODE_OK);
  324. errno = EPROTONOSUPPORT;
  325. return (SNMP_CODE_FAILED);
  326. }
  327. enum snmp_code
  328. snmp_calc_keychange(struct snmp_user *user __unused,
  329. uint8_t *keychange __unused)
  330. {
  331. errno = EPROTONOSUPPORT;
  332. return (SNMP_CODE_FAILED);
  333. }
  334. #endif /* HAVE_LIBCRYPTO */