1*91cadd71Stb /* $OpenBSD: usm.c,v 1.7 2022/01/05 16:41:07 tb Exp $ */
2b89ba26fSmartijn
3b89ba26fSmartijn /*
4b89ba26fSmartijn * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5b89ba26fSmartijn *
6b89ba26fSmartijn * Permission to use, copy, modify, and distribute this software for any
7b89ba26fSmartijn * purpose with or without fee is hereby granted, provided that the above
8b89ba26fSmartijn * copyright notice and this permission notice appear in all copies.
9b89ba26fSmartijn *
10b89ba26fSmartijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11b89ba26fSmartijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12b89ba26fSmartijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13b89ba26fSmartijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14b89ba26fSmartijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15b89ba26fSmartijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16b89ba26fSmartijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17b89ba26fSmartijn */
18b89ba26fSmartijn
19b89ba26fSmartijn #include <sys/time.h>
20b89ba26fSmartijn
21b89ba26fSmartijn #include <openssl/evp.h>
22b89ba26fSmartijn #include <openssl/hmac.h>
23b89ba26fSmartijn
24b89ba26fSmartijn #include <ber.h>
25b89ba26fSmartijn #include <errno.h>
26b89ba26fSmartijn #include <string.h>
27b89ba26fSmartijn #include <time.h>
28b89ba26fSmartijn
29b89ba26fSmartijn #include "smi.h"
30b89ba26fSmartijn #include "snmp.h"
31b89ba26fSmartijn #include "usm.h"
32b89ba26fSmartijn
33b89ba26fSmartijn #define USM_MAX_DIGESTLEN 48
34b89ba26fSmartijn #define USM_MAX_TIMEWINDOW 150
35b89ba26fSmartijn #define USM_SALTOFFSET 8
36b89ba26fSmartijn
37b89ba26fSmartijn struct usm_sec {
38b89ba26fSmartijn struct snmp_sec snmp;
39b89ba26fSmartijn char *user;
40b89ba26fSmartijn size_t userlen;
41b89ba26fSmartijn int engineidset;
42b89ba26fSmartijn char *engineid;
43b89ba26fSmartijn size_t engineidlen;
444f098f75Smartijn enum usm_key_level authlevel;
454f098f75Smartijn const EVP_MD *digest;
464f098f75Smartijn char *authkey;
47f5e30c24Smartijn enum usm_key_level privlevel;
48f5e30c24Smartijn const EVP_CIPHER *cipher;
49f5e30c24Smartijn char *privkey;
50b89ba26fSmartijn int bootsset;
51b89ba26fSmartijn uint32_t boots;
52b89ba26fSmartijn int timeset;
53b89ba26fSmartijn uint32_t time;
54b89ba26fSmartijn struct timespec timecheck;
55b89ba26fSmartijn };
56b89ba26fSmartijn
574f098f75Smartijn struct usm_cookie {
584f098f75Smartijn size_t digestoffset;
59f5e30c24Smartijn long long salt;
60f5e30c24Smartijn uint32_t boots;
61f5e30c24Smartijn uint32_t time;
624f098f75Smartijn };
634f098f75Smartijn
64b89ba26fSmartijn static int usm_doinit(struct snmp_agent *);
654f098f75Smartijn static char *usm_genparams(struct snmp_agent *, size_t *, void **);
664f098f75Smartijn static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *);
67f5e30c24Smartijn static struct ber_element *usm_encpdu(struct snmp_agent *agent,
68f5e30c24Smartijn struct ber_element *pdu, void *cookie);
69f5e30c24Smartijn static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *,
70f5e30c24Smartijn char *, size_t, size_t *);
71b89ba26fSmartijn static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
72f5e30c24Smartijn size_t, uint8_t, void **);
73f5e30c24Smartijn struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *);
744f098f75Smartijn static void usm_digest_pos(void *, size_t);
75b89ba26fSmartijn static void usm_free(void *);
764f098f75Smartijn static char *usm_passwd2mkey(const EVP_MD *, const char *);
774f098f75Smartijn static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
784f098f75Smartijn static size_t usm_digestlen(const EVP_MD *);
79b89ba26fSmartijn
80b89ba26fSmartijn struct snmp_sec *
usm_init(const char * user,size_t userlen)81b89ba26fSmartijn usm_init(const char *user, size_t userlen)
82b89ba26fSmartijn {
83b89ba26fSmartijn struct snmp_sec *sec;
84b89ba26fSmartijn struct usm_sec *usm;
85b89ba26fSmartijn
86b89ba26fSmartijn if (user == NULL || user[0] == '\0') {
87b89ba26fSmartijn errno = EINVAL;
88b89ba26fSmartijn return NULL;
89b89ba26fSmartijn }
90b89ba26fSmartijn
91b89ba26fSmartijn if ((sec = malloc(sizeof(*sec))) == NULL)
92b89ba26fSmartijn return NULL;
93b89ba26fSmartijn
94b89ba26fSmartijn if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) {
95b89ba26fSmartijn free(sec);
96b89ba26fSmartijn return NULL;
97b89ba26fSmartijn }
98b89ba26fSmartijn if ((usm->user = malloc(userlen)) == NULL) {
99b89ba26fSmartijn free(sec);
100b89ba26fSmartijn free(usm);
101b89ba26fSmartijn return NULL;
102b89ba26fSmartijn }
103b89ba26fSmartijn memcpy(usm->user, user, userlen);
104b89ba26fSmartijn usm->userlen = userlen;
105b89ba26fSmartijn
106b89ba26fSmartijn sec->model = SNMP_SEC_USM;
107b89ba26fSmartijn sec->init = usm_doinit;
108b89ba26fSmartijn sec->genparams = usm_genparams;
109f5e30c24Smartijn sec->encpdu = usm_encpdu;
110b89ba26fSmartijn sec->parseparams = usm_parseparams;
111f5e30c24Smartijn sec->decpdu = usm_decpdu;
1124f098f75Smartijn sec->finalparams = usm_finalparams;
113b89ba26fSmartijn sec->free = usm_free;
1144f098f75Smartijn sec->freecookie = free;
115b89ba26fSmartijn sec->data = usm;
116b89ba26fSmartijn return sec;
117b89ba26fSmartijn }
118b89ba26fSmartijn
119b89ba26fSmartijn static int
usm_doinit(struct snmp_agent * agent)120b89ba26fSmartijn usm_doinit(struct snmp_agent *agent)
121b89ba26fSmartijn {
122b89ba26fSmartijn struct ber_element *ber;
123b89ba26fSmartijn struct usm_sec *usm = agent->v3->sec->data;
124b89ba26fSmartijn int level;
125b89ba26fSmartijn size_t userlen;
126b89ba26fSmartijn
127b89ba26fSmartijn if (usm->engineidset && usm->bootsset && usm->timeset)
128b89ba26fSmartijn return 0;
129b89ba26fSmartijn
130b89ba26fSmartijn level = agent->v3->level;
131b89ba26fSmartijn agent->v3->level = SNMP_MSGFLAG_REPORT;
132b89ba26fSmartijn userlen = usm->userlen;
133b89ba26fSmartijn usm->userlen = 0;
134b89ba26fSmartijn
135b89ba26fSmartijn if ((ber = snmp_get(agent, NULL, 0)) == NULL) {
136b89ba26fSmartijn agent->v3->level = level;
137b89ba26fSmartijn usm->userlen = userlen;
138b89ba26fSmartijn return -1;
139b89ba26fSmartijn }
140696b5899Stb ober_free_element(ber);
141b89ba26fSmartijn
142b89ba26fSmartijn agent->v3->level = level;
143b89ba26fSmartijn usm->userlen = userlen;
144b89ba26fSmartijn
1458e8a231dSmartijn /*
1468e8a231dSmartijn * Ugly hack for HP Laserjet:
1478e8a231dSmartijn * This device returns the engineid on probing, but only returns boots
1488e8a231dSmartijn * and time after a packet has been sent with full auth/enc.
1498e8a231dSmartijn */
1508e8a231dSmartijn if (!usm->engineidset || !usm->bootsset || !usm->timeset) {
1518e8a231dSmartijn if ((ber = snmp_get(agent, NULL, 0)) == NULL)
1528e8a231dSmartijn return -1;
153696b5899Stb ober_free_element(ber);
1548e8a231dSmartijn }
155b89ba26fSmartijn return 0;
156b89ba26fSmartijn }
157b89ba26fSmartijn
158b89ba26fSmartijn static char *
usm_genparams(struct snmp_agent * agent,size_t * len,void ** cookie)1594f098f75Smartijn usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
160b89ba26fSmartijn {
161b89ba26fSmartijn struct ber ber;
1624f098f75Smartijn struct ber_element *params, *digestelm;
163b89ba26fSmartijn struct usm_sec *usm = agent->v3->sec->data;
1644f098f75Smartijn char digest[USM_MAX_DIGESTLEN];
165f5e30c24Smartijn size_t digestlen = 0, saltlen = 0;
166b89ba26fSmartijn char *secparams = NULL;
167b89ba26fSmartijn ssize_t berlen = 0;
1684f098f75Smartijn struct usm_cookie *usmcookie;
169b89ba26fSmartijn struct timespec now, timediff;
170b89ba26fSmartijn
1714f098f75Smartijn bzero(digest, sizeof(digest));
1724f098f75Smartijn
1734f098f75Smartijn if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL)
174b89ba26fSmartijn return NULL;
1754f098f75Smartijn *cookie = usmcookie;
1764f098f75Smartijn
177f5e30c24Smartijn arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt));
1784f098f75Smartijn if (usm->timeset) {
1794f098f75Smartijn if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
1804f098f75Smartijn free(usmcookie);
1814f098f75Smartijn return NULL;
1824f098f75Smartijn }
183b89ba26fSmartijn timespecsub(&now, &(usm->timecheck), &timediff);
184f5e30c24Smartijn usmcookie->time = usm->time + timediff.tv_sec;
185b89ba26fSmartijn } else
186f5e30c24Smartijn usmcookie->time = 0;
187f5e30c24Smartijn usmcookie->boots = usm->boots;
188f5e30c24Smartijn
1894f098f75Smartijn if (agent->v3->level & SNMP_MSGFLAG_AUTH)
1904f098f75Smartijn digestlen = usm_digestlen(usm->digest);
191f5e30c24Smartijn if (agent->v3->level & SNMP_MSGFLAG_PRIV)
192f5e30c24Smartijn saltlen = sizeof(usmcookie->salt);
193f5e30c24Smartijn
194696b5899Stb if ((params = ober_printf_elements(NULL, "{xddxxx}", usm->engineid,
195f5e30c24Smartijn usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user,
196f5e30c24Smartijn usm->userlen, digest, digestlen, &(usmcookie->salt),
197f5e30c24Smartijn saltlen)) == NULL) {
1984f098f75Smartijn free(usmcookie);
199b89ba26fSmartijn return NULL;
2004f098f75Smartijn }
2014f098f75Smartijn
202696b5899Stb if (ober_scanf_elements(params, "{SSSSe", &digestelm) == -1) {
203696b5899Stb ober_free_element(params);
2044f098f75Smartijn free(usmcookie);
2054f098f75Smartijn return NULL;
2064f098f75Smartijn }
2074f098f75Smartijn
208696b5899Stb ober_set_writecallback(digestelm, usm_digest_pos, usmcookie);
209b89ba26fSmartijn
210b89ba26fSmartijn bzero(&ber, sizeof(ber));
211696b5899Stb ober_set_application(&ber, smi_application);
212696b5899Stb if (ober_write_elements(&ber, params) != -1)
213b89ba26fSmartijn berlen = ber_copy_writebuf(&ber, (void **)&secparams);
214b89ba26fSmartijn
215b89ba26fSmartijn *len = berlen;
216696b5899Stb ober_free_element(params);
217696b5899Stb ober_free(&ber);
218b89ba26fSmartijn return secparams;
219b89ba26fSmartijn }
220b89ba26fSmartijn
221f5e30c24Smartijn static struct ber_element *
usm_encpdu(struct snmp_agent * agent,struct ber_element * pdu,void * cookie)222f5e30c24Smartijn usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie)
223f5e30c24Smartijn {
224f5e30c24Smartijn struct usm_sec *usm = agent->v3->sec->data;
225f5e30c24Smartijn struct usm_cookie *usmcookie = cookie;
226f5e30c24Smartijn struct ber ber;
227f5e30c24Smartijn struct ber_element *retpdu;
228f5e30c24Smartijn char *serialpdu, *encpdu;
229f5e30c24Smartijn ssize_t pdulen;
230f5e30c24Smartijn size_t encpdulen;
231f5e30c24Smartijn
232f5e30c24Smartijn bzero(&ber, sizeof(ber));
233696b5899Stb ober_set_application(&ber, smi_application);
234696b5899Stb pdulen = ober_write_elements(&ber, pdu);
235f5e30c24Smartijn if (pdulen == -1)
236f5e30c24Smartijn return NULL;
237f5e30c24Smartijn
238696b5899Stb ober_get_writebuf(&ber, (void **)&serialpdu);
239f5e30c24Smartijn
240f5e30c24Smartijn encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu,
241f5e30c24Smartijn pdulen, &encpdulen);
242696b5899Stb ober_free(&ber);
243f5e30c24Smartijn if (encpdu == NULL)
244f5e30c24Smartijn return NULL;
245f5e30c24Smartijn
246696b5899Stb retpdu = ober_add_nstring(NULL, encpdu, encpdulen);
247f5e30c24Smartijn free(encpdu);
248f5e30c24Smartijn return retpdu;
249f5e30c24Smartijn }
250f5e30c24Smartijn
251f5e30c24Smartijn static char *
usm_crypt(const EVP_CIPHER * cipher,int do_enc,char * key,struct usm_cookie * cookie,char * serialpdu,size_t pdulen,size_t * outlen)252f5e30c24Smartijn usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key,
253f5e30c24Smartijn struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t *outlen)
254f5e30c24Smartijn {
25558d76990Stb EVP_CIPHER_CTX *ctx;
256f5e30c24Smartijn size_t i;
257f5e30c24Smartijn char iv[EVP_MAX_IV_LENGTH];
258f5e30c24Smartijn char *salt = (char *)&(cookie->salt);
259f5e30c24Smartijn char *outtext;
260f5e30c24Smartijn int len, len2, bs;
261f5e30c24Smartijn uint32_t ivv;
262f5e30c24Smartijn
263f5e30c24Smartijn switch (EVP_CIPHER_type(cipher)) {
264f5e30c24Smartijn case NID_des_cbc:
265f5e30c24Smartijn /* RFC3414, chap 8.1.1.1. */
266f5e30c24Smartijn for (i = 0; i < 8; i++)
267f5e30c24Smartijn iv[i] = salt[i] ^ key[USM_SALTOFFSET + i];
268f5e30c24Smartijn break;
269f5e30c24Smartijn case NID_aes_128_cfb128:
270f5e30c24Smartijn /* RFC3826, chap 3.1.2.1. */
271f5e30c24Smartijn ivv = htobe32(cookie->boots);
272f5e30c24Smartijn memcpy(iv, &ivv, sizeof(ivv));
273f5e30c24Smartijn ivv = htobe32(cookie->time);
274f5e30c24Smartijn memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
275f5e30c24Smartijn memcpy(iv + 2 * sizeof(ivv), &(cookie->salt),
276f5e30c24Smartijn sizeof(cookie->salt));
277f5e30c24Smartijn break;
278f5e30c24Smartijn default:
279f5e30c24Smartijn return NULL;
280f5e30c24Smartijn }
281f5e30c24Smartijn
28258d76990Stb if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
283f5e30c24Smartijn return NULL;
284f5e30c24Smartijn
28558d76990Stb if (!EVP_CipherInit(ctx, cipher, key, iv, do_enc)) {
28658d76990Stb EVP_CIPHER_CTX_free(ctx);
28758d76990Stb return NULL;
28858d76990Stb }
28958d76990Stb
29058d76990Stb EVP_CIPHER_CTX_set_padding(ctx, do_enc);
291f5e30c24Smartijn
292f5e30c24Smartijn bs = EVP_CIPHER_block_size(cipher);
293f5e30c24Smartijn /* Maximum output size */
294f5e30c24Smartijn *outlen = pdulen + (bs - (pdulen % bs));
295f5e30c24Smartijn
29658d76990Stb if ((outtext = malloc(*outlen)) == NULL) {
29758d76990Stb EVP_CIPHER_CTX_free(ctx);
298f5e30c24Smartijn return NULL;
29958d76990Stb }
300f5e30c24Smartijn
30158d76990Stb if (EVP_CipherUpdate(ctx, outtext, &len, serialpdu, pdulen) &&
30258d76990Stb EVP_CipherFinal_ex(ctx, outtext + len, &len2))
303f5e30c24Smartijn *outlen = len + len2;
304f5e30c24Smartijn else {
305f5e30c24Smartijn free(outtext);
306f5e30c24Smartijn outtext = NULL;
307f5e30c24Smartijn }
308f5e30c24Smartijn
30958d76990Stb EVP_CIPHER_CTX_free(ctx);
310f5e30c24Smartijn
311f5e30c24Smartijn return outtext;
312f5e30c24Smartijn }
313f5e30c24Smartijn
314b89ba26fSmartijn static int
usm_finalparams(struct snmp_agent * agent,char * buf,size_t buflen,size_t secparamsoffset,void * cookie)3154f098f75Smartijn usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
3164f098f75Smartijn size_t secparamsoffset, void *cookie)
3174f098f75Smartijn {
3184f098f75Smartijn struct usm_sec *usm = agent->v3->sec->data;
3194f098f75Smartijn struct usm_cookie *usmcookie = cookie;
3204f098f75Smartijn u_char digest[EVP_MAX_MD_SIZE];
3214f098f75Smartijn
3224f098f75Smartijn if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0)
3234f098f75Smartijn return 0;
3244f098f75Smartijn
3254f098f75Smartijn if (usm->authlevel != USM_KEY_LOCALIZED)
3264f098f75Smartijn return -1;
3274f098f75Smartijn
3284f098f75Smartijn if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
3294f098f75Smartijn buflen, digest, NULL) == NULL)
3304f098f75Smartijn return -1;
3314f098f75Smartijn
3324f098f75Smartijn memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
3334f098f75Smartijn usm_digestlen(usm->digest));
3344f098f75Smartijn return 0;
3354f098f75Smartijn }
3364f098f75Smartijn
3374f098f75Smartijn static int
usm_parseparams(struct snmp_agent * agent,char * packet,size_t packetlen,off_t secparamsoffset,char * buf,size_t buflen,uint8_t level,void ** cookie)338b89ba26fSmartijn usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
339f5e30c24Smartijn off_t secparamsoffset, char *buf, size_t buflen, uint8_t level,
340f5e30c24Smartijn void **cookie)
341b89ba26fSmartijn {
342b89ba26fSmartijn struct usm_sec *usm = agent->v3->sec->data;
343b89ba26fSmartijn struct ber ber;
344b89ba26fSmartijn struct ber_element *secparams;
345f5e30c24Smartijn char *engineid, *user, *digest, *salt;
346f5e30c24Smartijn size_t engineidlen, userlen, digestlen, saltlen;
347b89ba26fSmartijn struct timespec now, timediff;
3484f098f75Smartijn off_t digestoffset;
3494f098f75Smartijn char exp_digest[EVP_MAX_MD_SIZE];
350f5e30c24Smartijn struct usm_cookie *usmcookie;
351b89ba26fSmartijn
352b89ba26fSmartijn bzero(&ber, sizeof(ber));
3534f098f75Smartijn bzero(exp_digest, sizeof(exp_digest));
354b89ba26fSmartijn
355696b5899Stb ober_set_application(&ber, smi_application);
356696b5899Stb ober_set_readbuf(&ber, buf, buflen);
357696b5899Stb if ((secparams = ober_read_elements(&ber, NULL)) == NULL)
358b89ba26fSmartijn return -1;
359696b5899Stb ober_free(&ber);
360b89ba26fSmartijn
361f5e30c24Smartijn if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL)
362b89ba26fSmartijn goto fail;
363f5e30c24Smartijn *cookie = usmcookie;
364f5e30c24Smartijn
365696b5899Stb if (ober_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen,
366f5e30c24Smartijn &(usmcookie->boots), &(usmcookie->time), &user, &userlen,
367f5e30c24Smartijn &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1)
368f5e30c24Smartijn goto fail;
369f5e30c24Smartijn if (saltlen != sizeof(usmcookie->salt) && saltlen != 0)
370f5e30c24Smartijn goto fail;
371f5e30c24Smartijn memcpy(&(usmcookie->salt), salt, saltlen);
372b89ba26fSmartijn
373b89ba26fSmartijn if (!usm->engineidset) {
374b89ba26fSmartijn if (usm_setengineid(agent->v3->sec, engineid,
375b89ba26fSmartijn engineidlen) == -1)
376b89ba26fSmartijn goto fail;
377b89ba26fSmartijn } else {
378b89ba26fSmartijn if (usm->engineidlen != engineidlen)
379b89ba26fSmartijn goto fail;
380b89ba26fSmartijn if (memcmp(usm->engineid, engineid, engineidlen) != 0)
381b89ba26fSmartijn goto fail;
382b89ba26fSmartijn }
383b89ba26fSmartijn
384b89ba26fSmartijn if (!usm->bootsset) {
385f5e30c24Smartijn usm->boots = usmcookie->boots;
386b89ba26fSmartijn usm->bootsset = 1;
387b89ba26fSmartijn } else {
388f5e30c24Smartijn if (usmcookie->boots < usm->boots)
389b89ba26fSmartijn goto fail;
390f5e30c24Smartijn if (usmcookie->boots > usm->boots) {
391b89ba26fSmartijn usm->bootsset = 0;
392b89ba26fSmartijn usm->timeset = 0;
393b89ba26fSmartijn usm_doinit(agent);
394b89ba26fSmartijn goto fail;
395b89ba26fSmartijn }
396b89ba26fSmartijn }
397b89ba26fSmartijn
398b89ba26fSmartijn if (!usm->timeset) {
399f5e30c24Smartijn usm->time = usmcookie->time;
400b89ba26fSmartijn if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1)
401b89ba26fSmartijn goto fail;
402b89ba26fSmartijn usm->timeset = 1;
403b89ba26fSmartijn } else {
404b89ba26fSmartijn if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
405b89ba26fSmartijn goto fail;
406b89ba26fSmartijn timespecsub(&now, &(usm->timecheck), &timediff);
407f5e30c24Smartijn if (usmcookie->time <
408f5e30c24Smartijn usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW ||
409f5e30c24Smartijn usmcookie->time >
410f5e30c24Smartijn usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) {
411b89ba26fSmartijn usm->bootsset = 0;
412b89ba26fSmartijn usm->timeset = 0;
413b89ba26fSmartijn usm_doinit(agent);
414b89ba26fSmartijn goto fail;
415b89ba26fSmartijn }
416b89ba26fSmartijn }
4178e8a231dSmartijn /*
4188e8a231dSmartijn * Don't assume these are set if both are zero.
4198e8a231dSmartijn * Ugly hack for HP Laserjet
4208e8a231dSmartijn */
4218e8a231dSmartijn if (usm->boots == 0 && usm->time == 0) {
4228e8a231dSmartijn usm->bootsset = 0;
4238e8a231dSmartijn usm->timeset = 0;
4248e8a231dSmartijn }
425b89ba26fSmartijn
426b89ba26fSmartijn if (userlen != usm->userlen ||
427b89ba26fSmartijn memcmp(user, usm->user, userlen) != 0)
428b89ba26fSmartijn goto fail;
429b89ba26fSmartijn
4304f098f75Smartijn if (level & SNMP_MSGFLAG_AUTH) {
4314f098f75Smartijn if (digestlen != usm_digestlen(usm->digest))
4324f098f75Smartijn goto fail;
4334f098f75Smartijn }
4344f098f75Smartijn if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) {
4354f098f75Smartijn bzero(packet + secparamsoffset + digestoffset, digestlen);
4364f098f75Smartijn if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet,
4374f098f75Smartijn packetlen, exp_digest, NULL) == NULL)
4384f098f75Smartijn goto fail;
4394f098f75Smartijn
4404f098f75Smartijn if (memcmp(exp_digest, digest, digestlen) != 0)
4414f098f75Smartijn goto fail;
4424f098f75Smartijn } else
4434f098f75Smartijn if (digestlen != 0)
4444f098f75Smartijn goto fail;
4454f098f75Smartijn
446696b5899Stb ober_free_element(secparams);
447b89ba26fSmartijn return 0;
448b89ba26fSmartijn
449b89ba26fSmartijn fail:
450f5e30c24Smartijn free(usmcookie);
451696b5899Stb ober_free_element(secparams);
452b89ba26fSmartijn return -1;
453b89ba26fSmartijn }
454b89ba26fSmartijn
455f5e30c24Smartijn struct ber_element *
usm_decpdu(struct snmp_agent * agent,char * encpdu,size_t encpdulen,void * cookie)456f5e30c24Smartijn usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie)
457f5e30c24Smartijn {
458f5e30c24Smartijn struct usm_sec *usm = agent->v3->sec->data;
459f5e30c24Smartijn struct usm_cookie *usmcookie = cookie;
460f5e30c24Smartijn struct ber ber;
461f5e30c24Smartijn struct ber_element *scopedpdu;
462f5e30c24Smartijn char *rawpdu;
463f5e30c24Smartijn size_t rawpdulen;
464f5e30c24Smartijn
465f5e30c24Smartijn if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie,
466f5e30c24Smartijn encpdu, encpdulen, &rawpdulen)) == NULL)
467f5e30c24Smartijn return NULL;
468f5e30c24Smartijn
469f5e30c24Smartijn bzero(&ber, sizeof(ber));
470696b5899Stb ober_set_application(&ber, smi_application);
471696b5899Stb ober_set_readbuf(&ber, rawpdu, rawpdulen);
472696b5899Stb scopedpdu = ober_read_elements(&ber, NULL);
473696b5899Stb ober_free(&ber);
474f5e30c24Smartijn free(rawpdu);
475f5e30c24Smartijn
476f5e30c24Smartijn return scopedpdu;
477f5e30c24Smartijn }
478f5e30c24Smartijn
479b89ba26fSmartijn static void
usm_digest_pos(void * data,size_t offset)4804f098f75Smartijn usm_digest_pos(void *data, size_t offset)
4814f098f75Smartijn {
4824f098f75Smartijn struct usm_cookie *usmcookie = data;
4834f098f75Smartijn
4844f098f75Smartijn usmcookie->digestoffset = offset;
4854f098f75Smartijn }
4864f098f75Smartijn
4874f098f75Smartijn static void
usm_free(void * data)488b89ba26fSmartijn usm_free(void *data)
489b89ba26fSmartijn {
490b89ba26fSmartijn struct usm_sec *usm = data;
491b89ba26fSmartijn
492b89ba26fSmartijn free(usm->user);
4934f098f75Smartijn free(usm->authkey);
494f5e30c24Smartijn free(usm->privkey);
495b89ba26fSmartijn free(usm->engineid);
496b89ba26fSmartijn free(usm);
497b89ba26fSmartijn }
498b89ba26fSmartijn
499b89ba26fSmartijn int
usm_setauth(struct snmp_sec * sec,const EVP_MD * digest,const char * key,size_t keylen,enum usm_key_level level)5004f098f75Smartijn usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
5014f098f75Smartijn size_t keylen, enum usm_key_level level)
5024f098f75Smartijn {
5034f098f75Smartijn struct usm_sec *usm = sec->data;
5044f098f75Smartijn char *lkey;
5054f098f75Smartijn
5064f098f75Smartijn /*
5074f098f75Smartijn * We could transform a master key to a local key here if we already
5084f098f75Smartijn * have usm_setengineid called. Sine snmpc.c is the only caller at
5094f098f75Smartijn * the moment there's no need, since it always calls this function
5104f098f75Smartijn * first.
5114f098f75Smartijn */
5124f098f75Smartijn if (level == USM_KEY_PASSWORD) {
5134f098f75Smartijn if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL)
5144f098f75Smartijn return -1;
5154f098f75Smartijn level = USM_KEY_MASTER;
5164f098f75Smartijn keylen = EVP_MD_size(digest);
5174f098f75Smartijn } else {
5184f098f75Smartijn if (keylen != (size_t)EVP_MD_size(digest)) {
5194f098f75Smartijn errno = EINVAL;
5204f098f75Smartijn return -1;
5214f098f75Smartijn }
5224f098f75Smartijn if ((lkey = malloc(keylen)) == NULL)
5234f098f75Smartijn return -1;
5244f098f75Smartijn memcpy(lkey, key, keylen);
5254f098f75Smartijn usm->authkey = lkey;
5264f098f75Smartijn }
5274f098f75Smartijn usm->digest = digest;
5284f098f75Smartijn usm->authlevel = level;
5294f098f75Smartijn return 0;
5304f098f75Smartijn }
5314f098f75Smartijn
5324f098f75Smartijn int
usm_setpriv(struct snmp_sec * sec,const EVP_CIPHER * cipher,const char * key,size_t keylen,enum usm_key_level level)533f5e30c24Smartijn usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key,
534f5e30c24Smartijn size_t keylen, enum usm_key_level level)
535f5e30c24Smartijn {
536f5e30c24Smartijn struct usm_sec *usm = sec->data;
537f5e30c24Smartijn char *lkey;
538f5e30c24Smartijn
539f5e30c24Smartijn if (usm->digest == NULL) {
540f5e30c24Smartijn errno = EINVAL;
541f5e30c24Smartijn return -1;
542f5e30c24Smartijn }
543f5e30c24Smartijn
544f5e30c24Smartijn /*
545f5e30c24Smartijn * We could transform a master key to a local key here if we already
546f5e30c24Smartijn * have usm_setengineid called. Sine snmpc.c is the only caller at
547f5e30c24Smartijn * the moment there's no need, since it always calls us first.
548f5e30c24Smartijn */
549f5e30c24Smartijn if (level == USM_KEY_PASSWORD) {
550f5e30c24Smartijn if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL)
551f5e30c24Smartijn return -1;
552f5e30c24Smartijn level = USM_KEY_MASTER;
553f5e30c24Smartijn keylen = EVP_MD_size(usm->digest);
554f5e30c24Smartijn } else {
555f5e30c24Smartijn if (keylen != (size_t)EVP_MD_size(usm->digest)) {
556f5e30c24Smartijn errno = EINVAL;
557f5e30c24Smartijn return -1;
558f5e30c24Smartijn }
559f5e30c24Smartijn if ((lkey = malloc(keylen)) == NULL)
560f5e30c24Smartijn return -1;
561f5e30c24Smartijn memcpy(lkey, key, keylen);
562f5e30c24Smartijn usm->privkey = lkey;
563f5e30c24Smartijn }
564f5e30c24Smartijn usm->cipher = cipher;
565f5e30c24Smartijn usm->privlevel = level;
566f5e30c24Smartijn return 0;
567f5e30c24Smartijn }
568f5e30c24Smartijn
569f5e30c24Smartijn int
usm_setengineid(struct snmp_sec * sec,char * engineid,size_t engineidlen)570b89ba26fSmartijn usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
571b89ba26fSmartijn {
572b89ba26fSmartijn struct usm_sec *usm = sec->data;
5734f098f75Smartijn char *mkey;
574b89ba26fSmartijn
575b89ba26fSmartijn if (usm->engineid != NULL)
576b89ba26fSmartijn free(usm->engineid);
577b89ba26fSmartijn if ((usm->engineid = malloc(engineidlen)) == NULL)
578b89ba26fSmartijn return -1;
579b89ba26fSmartijn memcpy(usm->engineid, engineid, engineidlen);
580b89ba26fSmartijn usm->engineidlen = engineidlen;
581b89ba26fSmartijn usm->engineidset = 1;
582b89ba26fSmartijn
5834f098f75Smartijn if (usm->authlevel == USM_KEY_MASTER) {
5844f098f75Smartijn mkey = usm->authkey;
5854f098f75Smartijn if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
5864f098f75Smartijn mkey)) == NULL) {
5874f098f75Smartijn usm->authkey = mkey;
5884f098f75Smartijn return -1;
5894f098f75Smartijn }
5904f098f75Smartijn free(mkey);
5914f098f75Smartijn usm->authlevel = USM_KEY_LOCALIZED;
5924f098f75Smartijn }
593f5e30c24Smartijn if (usm->privlevel == USM_KEY_MASTER) {
594f5e30c24Smartijn mkey = usm->privkey;
595f5e30c24Smartijn if ((usm->privkey = usm_mkey2lkey(usm, usm->digest,
596f5e30c24Smartijn mkey)) == NULL) {
597f5e30c24Smartijn usm->privkey = mkey;
598f5e30c24Smartijn return -1;
599f5e30c24Smartijn }
600f5e30c24Smartijn free(mkey);
601f5e30c24Smartijn usm->privlevel = USM_KEY_LOCALIZED;
602f5e30c24Smartijn }
6034f098f75Smartijn
604b89ba26fSmartijn return 0;
605b89ba26fSmartijn }
606b89ba26fSmartijn
607b89ba26fSmartijn int
usm_setbootstime(struct snmp_sec * sec,uint32_t boots,uint32_t time)608b89ba26fSmartijn usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
609b89ba26fSmartijn {
610b89ba26fSmartijn struct usm_sec *usm = sec->data;
611b89ba26fSmartijn
612b89ba26fSmartijn if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1)
613b89ba26fSmartijn return -1;
614b89ba26fSmartijn
615b89ba26fSmartijn usm->boots = boots;
616b89ba26fSmartijn usm->bootsset = 1;
617b89ba26fSmartijn usm->time = time;
618b89ba26fSmartijn usm->timeset = 1;
619b89ba26fSmartijn return 0;
620b89ba26fSmartijn }
6214f098f75Smartijn
6224f098f75Smartijn static char *
usm_passwd2mkey(const EVP_MD * md,const char * passwd)6234f098f75Smartijn usm_passwd2mkey(const EVP_MD *md, const char *passwd)
6244f098f75Smartijn {
62558d76990Stb EVP_MD_CTX *ctx;
6264f098f75Smartijn int i, count;
6274f098f75Smartijn const u_char *pw;
6284f098f75Smartijn u_char *c;
6294f098f75Smartijn u_char keybuf[EVP_MAX_MD_SIZE];
6304f098f75Smartijn unsigned dlen;
6314f098f75Smartijn char *key;
6324f098f75Smartijn
63358d76990Stb if ((ctx = EVP_MD_CTX_new()) == NULL)
63458d76990Stb return NULL;
635*91cadd71Stb if (!EVP_DigestInit_ex(ctx, md, NULL)) {
636*91cadd71Stb EVP_MD_CTX_free(ctx);
637*91cadd71Stb return NULL;
638*91cadd71Stb }
639*91cadd71Stb
6404f098f75Smartijn pw = (const u_char *)passwd;
6414f098f75Smartijn for (count = 0; count < 1048576; count += 64) {
6424f098f75Smartijn c = keybuf;
6434f098f75Smartijn for (i = 0; i < 64; i++) {
6444f098f75Smartijn if (*pw == '\0')
6454f098f75Smartijn pw = (const u_char *)passwd;
6464f098f75Smartijn *c++ = *pw++;
6474f098f75Smartijn }
648*91cadd71Stb if (!EVP_DigestUpdate(ctx, keybuf, 64)) {
649*91cadd71Stb EVP_MD_CTX_free(ctx);
650*91cadd71Stb return NULL;
6514f098f75Smartijn }
652*91cadd71Stb }
653*91cadd71Stb if (!EVP_DigestFinal_ex(ctx, keybuf, &dlen)) {
654*91cadd71Stb EVP_MD_CTX_free(ctx);
655*91cadd71Stb return NULL;
656*91cadd71Stb }
65758d76990Stb EVP_MD_CTX_free(ctx);
6584f098f75Smartijn
6594f098f75Smartijn if ((key = malloc(dlen)) == NULL)
6604f098f75Smartijn return NULL;
6614f098f75Smartijn memcpy(key, keybuf, dlen);
6624f098f75Smartijn return key;
6634f098f75Smartijn }
6644f098f75Smartijn
6654f098f75Smartijn static char *
usm_mkey2lkey(struct usm_sec * usm,const EVP_MD * md,const char * mkey)6664f098f75Smartijn usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
6674f098f75Smartijn {
66858d76990Stb EVP_MD_CTX *ctx;
6694f098f75Smartijn u_char buf[EVP_MAX_MD_SIZE];
6704f098f75Smartijn u_char *lkey;
6714f098f75Smartijn unsigned lklen;
6724f098f75Smartijn
67358d76990Stb if ((ctx = EVP_MD_CTX_new()) == NULL)
67458d76990Stb return NULL;
6754f098f75Smartijn
676*91cadd71Stb if (!EVP_DigestInit_ex(ctx, md, NULL) ||
677*91cadd71Stb !EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md)) ||
678*91cadd71Stb !EVP_DigestUpdate(ctx, usm->engineid, usm->engineidlen) ||
679*91cadd71Stb !EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md)) ||
680*91cadd71Stb !EVP_DigestFinal_ex(ctx, buf, &lklen)) {
681*91cadd71Stb EVP_MD_CTX_free(ctx);
682*91cadd71Stb return NULL;
683*91cadd71Stb }
6844f098f75Smartijn
68558d76990Stb EVP_MD_CTX_free(ctx);
6864f098f75Smartijn
6874f098f75Smartijn if ((lkey = malloc(lklen)) == NULL)
6884f098f75Smartijn return NULL;
6894f098f75Smartijn memcpy(lkey, buf, lklen);
6904f098f75Smartijn return lkey;
6914f098f75Smartijn }
6924f098f75Smartijn
6934f098f75Smartijn static size_t
usm_digestlen(const EVP_MD * md)6944f098f75Smartijn usm_digestlen(const EVP_MD *md)
6954f098f75Smartijn {
6964f098f75Smartijn switch (EVP_MD_type(md)) {
6974f098f75Smartijn case NID_md5:
6984f098f75Smartijn case NID_sha1:
6994f098f75Smartijn return 12;
7004f098f75Smartijn case NID_sha224:
7014f098f75Smartijn return 16;
7024f098f75Smartijn case NID_sha256:
7034f098f75Smartijn return 24;
7044f098f75Smartijn case NID_sha384:
7054f098f75Smartijn return 32;
7064f098f75Smartijn case NID_sha512:
7074f098f75Smartijn return 48;
7084f098f75Smartijn default:
7094f098f75Smartijn return 0;
7104f098f75Smartijn }
7114f098f75Smartijn }
712