xref: /openbsd/usr.bin/snmp/snmp.c (revision f2fa7a92)
1*f2fa7a92Sderaadt /*	$OpenBSD: snmp.c,v 1.11 2020/08/02 20:14:10 deraadt Exp $	*/
2442e4f4fSmartijn 
3442e4f4fSmartijn /*
4442e4f4fSmartijn  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5442e4f4fSmartijn  * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
6442e4f4fSmartijn  *
7442e4f4fSmartijn  * Permission to use, copy, modify, and distribute this software for any
8442e4f4fSmartijn  * purpose with or without fee is hereby granted, provided that the above
9442e4f4fSmartijn  * copyright notice and this permission notice appear in all copies.
10442e4f4fSmartijn  *
11442e4f4fSmartijn  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12442e4f4fSmartijn  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13442e4f4fSmartijn  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14442e4f4fSmartijn  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15442e4f4fSmartijn  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16442e4f4fSmartijn  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17442e4f4fSmartijn  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18442e4f4fSmartijn  */
19442e4f4fSmartijn 
20442e4f4fSmartijn #include <sys/socket.h>
21442e4f4fSmartijn 
22442e4f4fSmartijn #include <errno.h>
23442e4f4fSmartijn #include <poll.h>
24442e4f4fSmartijn #include <stdlib.h>
25442e4f4fSmartijn #include <string.h>
26442e4f4fSmartijn #include <stdio.h>
27442e4f4fSmartijn #include <time.h>
28442e4f4fSmartijn 
29442e4f4fSmartijn #include "ber.h"
30442e4f4fSmartijn #include "smi.h"
31442e4f4fSmartijn #include "snmp.h"
32442e4f4fSmartijn 
33b89ba26fSmartijn #define UDP_MAXPACKET 65535
34b89ba26fSmartijn 
35442e4f4fSmartijn static struct ber_element *
36442e4f4fSmartijn     snmp_resolve(struct snmp_agent *, struct ber_element *, int);
37e13f0058Smartijn static char *
38e13f0058Smartijn     snmp_package(struct snmp_agent *, struct ber_element *, size_t *);
39e13f0058Smartijn static struct ber_element *
40e13f0058Smartijn     snmp_unpackage(struct snmp_agent *, char *, size_t);
41b89ba26fSmartijn static void snmp_v3_free(struct snmp_v3 *);
424f098f75Smartijn static void snmp_v3_secparamsoffset(void *, size_t);
43b89ba26fSmartijn 
44b89ba26fSmartijn struct snmp_v3 *
snmp_v3_init(int level,const char * ctxname,size_t ctxnamelen,struct snmp_sec * sec)45b89ba26fSmartijn snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
46b89ba26fSmartijn     struct snmp_sec *sec)
47b89ba26fSmartijn {
48b89ba26fSmartijn 	struct snmp_v3 *v3;
49b89ba26fSmartijn 
50b89ba26fSmartijn 	if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level ||
51b89ba26fSmartijn 	    sec == NULL) {
52b89ba26fSmartijn 		errno = EINVAL;
53b89ba26fSmartijn 		return NULL;
54b89ba26fSmartijn 	}
55b89ba26fSmartijn 	if ((v3 = calloc(1, sizeof(*v3))) == NULL)
56b89ba26fSmartijn 		return NULL;
57b89ba26fSmartijn 
58b89ba26fSmartijn 	v3->level = level | SNMP_MSGFLAG_REPORT;
59b89ba26fSmartijn 	v3->ctxnamelen = ctxnamelen;
60b89ba26fSmartijn 	if (ctxnamelen != 0) {
61b89ba26fSmartijn 		if ((v3->ctxname = malloc(ctxnamelen)) == NULL) {
62b89ba26fSmartijn 			free(v3);
63b89ba26fSmartijn 			return NULL;
64b89ba26fSmartijn 		}
65b89ba26fSmartijn 		memcpy(v3->ctxname, ctxname, ctxnamelen);
66b89ba26fSmartijn 	}
67b89ba26fSmartijn 	v3->sec = sec;
68b89ba26fSmartijn 	return v3;
69b89ba26fSmartijn }
70b89ba26fSmartijn 
71b89ba26fSmartijn int
snmp_v3_setengineid(struct snmp_v3 * v3,char * engineid,size_t engineidlen)72b89ba26fSmartijn snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen)
73b89ba26fSmartijn {
74b89ba26fSmartijn 	if (v3->engineidset)
75b89ba26fSmartijn 		free(v3->engineid);
76b89ba26fSmartijn 	if ((v3->engineid = malloc(engineidlen)) == NULL)
77b89ba26fSmartijn 		return -1;
78b89ba26fSmartijn 	memcpy(v3->engineid, engineid, engineidlen);
79b89ba26fSmartijn 	v3->engineidlen = engineidlen;
80b89ba26fSmartijn 	v3->engineidset = 1;
81b89ba26fSmartijn 	return 0;
82b89ba26fSmartijn }
83442e4f4fSmartijn 
84442e4f4fSmartijn struct snmp_agent *
snmp_connect_v12(int fd,enum snmp_version version,const char * community)85442e4f4fSmartijn snmp_connect_v12(int fd, enum snmp_version version, const char *community)
86442e4f4fSmartijn {
87442e4f4fSmartijn 	struct snmp_agent *agent;
88442e4f4fSmartijn 
89442e4f4fSmartijn 	if (version != SNMP_V1 && version != SNMP_V2C) {
90442e4f4fSmartijn 		errno = EINVAL;
91442e4f4fSmartijn 		return NULL;
92442e4f4fSmartijn 	}
93442e4f4fSmartijn 	if ((agent = malloc(sizeof(*agent))) == NULL)
94442e4f4fSmartijn 		return NULL;
95442e4f4fSmartijn 	agent->fd = fd;
96442e4f4fSmartijn 	agent->version = version;
97442e4f4fSmartijn 	if ((agent->community = strdup(community)) == NULL)
98442e4f4fSmartijn 		goto fail;
99442e4f4fSmartijn 	agent->timeout = 1;
100442e4f4fSmartijn 	agent->retries = 5;
101b89ba26fSmartijn 	agent->v3 = NULL;
102442e4f4fSmartijn 	return agent;
103442e4f4fSmartijn 
104442e4f4fSmartijn fail:
105442e4f4fSmartijn 	free(agent);
106442e4f4fSmartijn 	return NULL;
107442e4f4fSmartijn }
108442e4f4fSmartijn 
109b89ba26fSmartijn struct snmp_agent *
snmp_connect_v3(int fd,struct snmp_v3 * v3)110b89ba26fSmartijn snmp_connect_v3(int fd, struct snmp_v3 *v3)
111b89ba26fSmartijn {
112b89ba26fSmartijn 	struct snmp_agent *agent;
113b89ba26fSmartijn 
114b89ba26fSmartijn 	if ((agent = malloc(sizeof(*agent))) == NULL)
115b89ba26fSmartijn 		return NULL;
116b89ba26fSmartijn 	agent->fd = fd;
117b89ba26fSmartijn 	agent->version = SNMP_V3;
118b89ba26fSmartijn 	agent->v3 = v3;
119b89ba26fSmartijn 	agent->timeout = 1;
120b89ba26fSmartijn 	agent->retries = 5;
121b89ba26fSmartijn 	agent->community = NULL;
122b89ba26fSmartijn 
123b89ba26fSmartijn 	if (v3->sec->init(agent) == -1) {
124b89ba26fSmartijn 		snmp_free_agent(agent);
125b89ba26fSmartijn 		return NULL;
126b89ba26fSmartijn 	}
127b89ba26fSmartijn 	return agent;
128b89ba26fSmartijn }
129b89ba26fSmartijn 
130442e4f4fSmartijn void
snmp_free_agent(struct snmp_agent * agent)131442e4f4fSmartijn snmp_free_agent(struct snmp_agent *agent)
132442e4f4fSmartijn {
133442e4f4fSmartijn 	free(agent->community);
134b89ba26fSmartijn 	if (agent->v3 != NULL)
135b89ba26fSmartijn 		snmp_v3_free(agent->v3);
136442e4f4fSmartijn 	free(agent);
137442e4f4fSmartijn }
138442e4f4fSmartijn 
139b89ba26fSmartijn static void
snmp_v3_free(struct snmp_v3 * v3)140b89ba26fSmartijn snmp_v3_free(struct snmp_v3 *v3)
141b89ba26fSmartijn {
142b89ba26fSmartijn 	v3->sec->free(v3->sec->data);
143b89ba26fSmartijn 	free(v3->sec);
144b89ba26fSmartijn 	free(v3->ctxname);
145b89ba26fSmartijn 	free(v3->engineid);
146b89ba26fSmartijn 	free(v3);
147b89ba26fSmartijn }
148b89ba26fSmartijn 
149442e4f4fSmartijn struct ber_element *
snmp_get(struct snmp_agent * agent,struct ber_oid * oid,size_t len)150442e4f4fSmartijn snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
151442e4f4fSmartijn {
152442e4f4fSmartijn 	struct ber_element *pdu, *varbind;
153442e4f4fSmartijn 	size_t i;
154442e4f4fSmartijn 
155696b5899Stb 	if ((pdu = ober_add_sequence(NULL)) == NULL)
156442e4f4fSmartijn 		return NULL;
157696b5899Stb 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
158442e4f4fSmartijn 	    SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
159442e4f4fSmartijn 		goto fail;
160*f2fa7a92Sderaadt 	for (i = 0; i < len; i++) {
161696b5899Stb 		varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
162442e4f4fSmartijn 		if (varbind == NULL)
163442e4f4fSmartijn 			goto fail;
164*f2fa7a92Sderaadt 	}
165442e4f4fSmartijn 
166442e4f4fSmartijn 	return snmp_resolve(agent, pdu, 1);
167442e4f4fSmartijn fail:
168696b5899Stb 	ober_free_elements(pdu);
169442e4f4fSmartijn 	return NULL;
170442e4f4fSmartijn }
171442e4f4fSmartijn 
172442e4f4fSmartijn struct ber_element *
snmp_getnext(struct snmp_agent * agent,struct ber_oid * oid,size_t len)173442e4f4fSmartijn snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
174442e4f4fSmartijn {
175442e4f4fSmartijn 	struct ber_element *pdu, *varbind;
176442e4f4fSmartijn 	size_t i;
177442e4f4fSmartijn 
178696b5899Stb 	if ((pdu = ober_add_sequence(NULL)) == NULL)
179442e4f4fSmartijn 		return NULL;
180696b5899Stb 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
181442e4f4fSmartijn 	    SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
182442e4f4fSmartijn 		goto fail;
183*f2fa7a92Sderaadt 	for (i = 0; i < len; i++) {
184696b5899Stb 		varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
185442e4f4fSmartijn 		if (varbind == NULL)
186442e4f4fSmartijn 			goto fail;
187*f2fa7a92Sderaadt 	}
188442e4f4fSmartijn 
189442e4f4fSmartijn 	return snmp_resolve(agent, pdu, 1);
190442e4f4fSmartijn fail:
191696b5899Stb 	ober_free_elements(pdu);
192442e4f4fSmartijn 	return NULL;
193442e4f4fSmartijn }
194442e4f4fSmartijn 
195442e4f4fSmartijn int
snmp_trap(struct snmp_agent * agent,struct timespec * uptime,struct ber_oid * oid,struct ber_element * custvarbind)196442e4f4fSmartijn snmp_trap(struct snmp_agent *agent, struct timespec *uptime,
197442e4f4fSmartijn     struct ber_oid *oid, struct ber_element *custvarbind)
198442e4f4fSmartijn {
199442e4f4fSmartijn 	struct ber_element *pdu, *varbind;
200442e4f4fSmartijn 	struct ber_oid sysuptime, trap;
201442e4f4fSmartijn 	long long ticks;
202442e4f4fSmartijn 
203696b5899Stb 	if ((pdu = ober_add_sequence(NULL)) == NULL)
204442e4f4fSmartijn 		return -1;
205696b5899Stb 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
206442e4f4fSmartijn 	    SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL)
207442e4f4fSmartijn 		goto fail;
208442e4f4fSmartijn 
209442e4f4fSmartijn 	ticks = uptime->tv_sec * 100;
210442e4f4fSmartijn 	ticks += uptime->tv_nsec / 10000000;
211442e4f4fSmartijn 	if (smi_string2oid("sysUpTime.0", &sysuptime) == -1)
212442e4f4fSmartijn 		goto fail;
213696b5899Stb 	if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
214442e4f4fSmartijn 	    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
215442e4f4fSmartijn 		goto fail;
216442e4f4fSmartijn 	if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
217442e4f4fSmartijn 		goto fail;
218696b5899Stb 	if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
219442e4f4fSmartijn 		goto fail;
220442e4f4fSmartijn 	if (custvarbind != NULL)
221696b5899Stb 		ober_link_elements(varbind, custvarbind);
222442e4f4fSmartijn 
223442e4f4fSmartijn 	snmp_resolve(agent, pdu, 0);
224442e4f4fSmartijn 	return 0;
225442e4f4fSmartijn fail:
226696b5899Stb 	ober_free_elements(pdu);
227442e4f4fSmartijn 	return -1;
228442e4f4fSmartijn }
229442e4f4fSmartijn 
230442e4f4fSmartijn struct ber_element *
snmp_getbulk(struct snmp_agent * agent,struct ber_oid * oid,size_t len,int non_repeaters,int max_repetitions)231442e4f4fSmartijn snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len,
232442e4f4fSmartijn     int non_repeaters, int max_repetitions)
233442e4f4fSmartijn {
234442e4f4fSmartijn 	struct ber_element *pdu, *varbind;
235442e4f4fSmartijn 	size_t i;
236442e4f4fSmartijn 
237696b5899Stb 	if ((pdu = ober_add_sequence(NULL)) == NULL)
238442e4f4fSmartijn 		return NULL;
239696b5899Stb 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
240442e4f4fSmartijn 	    SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
241442e4f4fSmartijn 	    max_repetitions)) == NULL)
242442e4f4fSmartijn 		goto fail;
243*f2fa7a92Sderaadt 	for (i = 0; i < len; i++) {
244696b5899Stb 		varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
245442e4f4fSmartijn 		if (varbind == NULL)
246442e4f4fSmartijn 			goto fail;
247*f2fa7a92Sderaadt 	}
248442e4f4fSmartijn 
249442e4f4fSmartijn 	return snmp_resolve(agent, pdu, 1);
250442e4f4fSmartijn fail:
251696b5899Stb 	ober_free_elements(pdu);
252442e4f4fSmartijn 	return NULL;
253442e4f4fSmartijn }
254442e4f4fSmartijn 
255ecb1f8acSmartijn struct ber_element *
snmp_set(struct snmp_agent * agent,struct ber_element * vblist)256ecb1f8acSmartijn snmp_set(struct snmp_agent *agent, struct ber_element *vblist)
257ecb1f8acSmartijn {
258ecb1f8acSmartijn 	struct ber_element *pdu;
259ecb1f8acSmartijn 
260696b5899Stb 	if ((pdu = ober_add_sequence(NULL)) == NULL)
261ecb1f8acSmartijn 		return NULL;
262696b5899Stb 	if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT,
263ecb1f8acSmartijn 	    SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) {
264696b5899Stb 		ober_free_elements(pdu);
265696b5899Stb 		ober_free_elements(vblist);
266ecb1f8acSmartijn 		return NULL;
267ecb1f8acSmartijn 	}
268ecb1f8acSmartijn 
269ecb1f8acSmartijn 	return snmp_resolve(agent, pdu, 1);
270ecb1f8acSmartijn }
271ecb1f8acSmartijn 
272442e4f4fSmartijn static struct ber_element *
snmp_resolve(struct snmp_agent * agent,struct ber_element * pdu,int reply)273442e4f4fSmartijn snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
274442e4f4fSmartijn {
275e13f0058Smartijn 	struct ber_element *varbind;
276442e4f4fSmartijn 	struct ber_oid oid;
277442e4f4fSmartijn 	struct timespec start, now;
278442e4f4fSmartijn 	struct pollfd pfd;
279e13f0058Smartijn 	char *message;
280442e4f4fSmartijn 	ssize_t len;
281442e4f4fSmartijn 	long long reqid, rreqid;
282442e4f4fSmartijn 	short direction;
283442e4f4fSmartijn 	int to, nfds, ret;
284442e4f4fSmartijn 	int tries;
285442e4f4fSmartijn 	char buf[READ_BUF_SIZE];
286442e4f4fSmartijn 
287696b5899Stb 	if (ober_scanf_elements(pdu, "{i", &reqid) != 0) {
288442e4f4fSmartijn 		errno = EINVAL;
289696b5899Stb 		ober_free_elements(pdu);
290442e4f4fSmartijn 		return NULL;
291442e4f4fSmartijn 	}
292442e4f4fSmartijn 
293e13f0058Smartijn 	if ((message = snmp_package(agent, pdu, &len)) == NULL)
294442e4f4fSmartijn 		return NULL;
295442e4f4fSmartijn 
296442e4f4fSmartijn 	clock_gettime(CLOCK_MONOTONIC, &start);
297442e4f4fSmartijn 	memcpy(&now, &start, sizeof(now));
298442e4f4fSmartijn 	direction = POLLOUT;
299442e4f4fSmartijn 	tries = agent->retries + 1;
300442e4f4fSmartijn 	while (tries) {
301442e4f4fSmartijn 		pfd.fd = agent->fd;
302442e4f4fSmartijn 		pfd.events = direction;
303442e4f4fSmartijn 		if (agent->timeout > 0) {
304442e4f4fSmartijn 			to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000;
305442e4f4fSmartijn 			to -= (now.tv_nsec - start.tv_nsec) / 1000000;
306442e4f4fSmartijn 		} else
307442e4f4fSmartijn 			to = INFTIM;
308442e4f4fSmartijn 		nfds = poll(&pfd, 1, to);
309442e4f4fSmartijn 		if (nfds == 0) {
310442e4f4fSmartijn 			errno = ETIMEDOUT;
311442e4f4fSmartijn 			direction = POLLOUT;
312442e4f4fSmartijn 			tries--;
313442e4f4fSmartijn 			continue;
314442e4f4fSmartijn 		}
315442e4f4fSmartijn 		if (nfds == -1) {
316442e4f4fSmartijn 			if (errno == EINTR)
317442e4f4fSmartijn 				continue;
318442e4f4fSmartijn 			else
319442e4f4fSmartijn 				goto fail;
320442e4f4fSmartijn 		}
321442e4f4fSmartijn 		if (direction == POLLOUT) {
322e13f0058Smartijn 			ret = send(agent->fd, message, len, MSG_DONTWAIT);
323442e4f4fSmartijn 			if (ret == -1)
324442e4f4fSmartijn 				goto fail;
325442e4f4fSmartijn 			if (ret < len) {
326442e4f4fSmartijn 				errno = EBADMSG;
327442e4f4fSmartijn 				goto fail;
328442e4f4fSmartijn 			}
329442e4f4fSmartijn 			if (!reply)
330442e4f4fSmartijn 				return NULL;
331442e4f4fSmartijn 			direction = POLLIN;
332442e4f4fSmartijn 			continue;
333442e4f4fSmartijn 		}
334442e4f4fSmartijn 		ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT);
335442e4f4fSmartijn 		if (ret == 0)
336442e4f4fSmartijn 			errno = ECONNRESET;
337442e4f4fSmartijn 		if (ret <= 0)
338442e4f4fSmartijn 			goto fail;
339e13f0058Smartijn 		if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
340b0db1fdeSmartijn 			tries--;
341e13f0058Smartijn 			direction = POLLOUT;
342b0db1fdeSmartijn 			errno = EPROTO;
343442e4f4fSmartijn 			continue;
344b0db1fdeSmartijn 		}
345442e4f4fSmartijn 		/* Validate pdu format and check request id */
346696b5899Stb 		if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
347b0db1fdeSmartijn 		    varbind->be_encoding != BER_TYPE_SEQUENCE) {
348b0db1fdeSmartijn 			errno = EPROTO;
349b0db1fdeSmartijn 			direction = POLLOUT;
350b0db1fdeSmartijn 			tries--;
351442e4f4fSmartijn 			continue;
352b0db1fdeSmartijn 		}
353b89ba26fSmartijn 		if (rreqid != reqid && rreqid != 0) {
354b0db1fdeSmartijn 			errno = EPROTO;
355b0db1fdeSmartijn 			direction = POLLOUT;
356b0db1fdeSmartijn 			tries--;
357b0db1fdeSmartijn 			continue;
358b0db1fdeSmartijn 		}
359442e4f4fSmartijn 		for (varbind = varbind->be_sub; varbind != NULL;
360442e4f4fSmartijn 		    varbind = varbind->be_next) {
361696b5899Stb 			if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) {
362b0db1fdeSmartijn 				errno = EPROTO;
363b0db1fdeSmartijn 				direction = POLLOUT;
364b0db1fdeSmartijn 				tries--;
365e6666952Smartijn 				break;
366e6666952Smartijn 			}
367e6666952Smartijn 		}
368e6666952Smartijn 		if (varbind != NULL)
369b89ba26fSmartijn 			continue;
370442e4f4fSmartijn 
371e13f0058Smartijn 		free(message);
372442e4f4fSmartijn 		return pdu;
373442e4f4fSmartijn 	}
374442e4f4fSmartijn 
375442e4f4fSmartijn fail:
376e13f0058Smartijn 	free(message);
377442e4f4fSmartijn 	return NULL;
378442e4f4fSmartijn }
379e13f0058Smartijn 
380e13f0058Smartijn static char *
snmp_package(struct snmp_agent * agent,struct ber_element * pdu,size_t * len)381e13f0058Smartijn snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
382e13f0058Smartijn {
383e13f0058Smartijn 	struct ber ber;
384f5e30c24Smartijn 	struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
385b89ba26fSmartijn 	ssize_t securitysize, ret;
3864f098f75Smartijn 	size_t secparamsoffset;
387b89ba26fSmartijn 	char *securityparams = NULL, *packet = NULL;
388b89ba26fSmartijn 	long long msgid;
3894f098f75Smartijn 	void *cookie = NULL;
390e13f0058Smartijn 
391e13f0058Smartijn 	bzero(&ber, sizeof(ber));
392696b5899Stb 	ober_set_application(&ber, smi_application);
393e13f0058Smartijn 
394696b5899Stb 	if ((message = ober_add_sequence(NULL)) == NULL) {
395696b5899Stb 		ober_free_elements(pdu);
396e13f0058Smartijn 		goto fail;
397e13f0058Smartijn 	}
398e13f0058Smartijn 
399e13f0058Smartijn 	switch (agent->version) {
400e13f0058Smartijn 	case SNMP_V1:
401e13f0058Smartijn 	case SNMP_V2C:
402696b5899Stb 		if (ober_printf_elements(message, "dse", agent->version,
403e13f0058Smartijn 		    agent->community, pdu) == NULL) {
404696b5899Stb 			ober_free_elements(pdu);
405e13f0058Smartijn 			goto fail;
406e13f0058Smartijn 		}
407e13f0058Smartijn 		break;
408e13f0058Smartijn 	case SNMP_V3:
409b89ba26fSmartijn 		msgid = arc4random_uniform(2147483647);
410696b5899Stb 		if ((scopedpdu = ober_add_sequence(NULL)) == NULL) {
411696b5899Stb 			ober_free_elements(pdu);
412b89ba26fSmartijn 			goto fail;
413b89ba26fSmartijn 		}
414696b5899Stb 		if (ober_printf_elements(scopedpdu, "xxe",
415b89ba26fSmartijn 		    agent->v3->engineid, agent->v3->engineidlen,
416b89ba26fSmartijn 		    agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
417696b5899Stb 			ober_free_elements(pdu);
418696b5899Stb 			ober_free_elements(scopedpdu);
419b89ba26fSmartijn 			goto fail;
420b89ba26fSmartijn 		}
421b89ba26fSmartijn 		pdu = NULL;
422b89ba26fSmartijn 		if ((securityparams = agent->v3->sec->genparams(agent,
4234f098f75Smartijn 		    &securitysize, &cookie)) == NULL) {
424696b5899Stb 			ober_free_elements(scopedpdu);
425b89ba26fSmartijn 			goto fail;
426b89ba26fSmartijn 		}
427f5e30c24Smartijn 		if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
428f5e30c24Smartijn 			if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
429f5e30c24Smartijn 			    cookie)) == NULL)
430f5e30c24Smartijn 				goto fail;
431696b5899Stb 			ober_free_elements(scopedpdu);
432f5e30c24Smartijn 			scopedpdu = encpdu;
433f5e30c24Smartijn 		}
434696b5899Stb 		if (ober_printf_elements(message, "d{idxd}xe",
435b89ba26fSmartijn 		    agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
436b89ba26fSmartijn 		    (size_t) 1, agent->v3->sec->model, securityparams,
437a99ea7faSmartijn 		    securitysize, scopedpdu) == NULL) {
438696b5899Stb 			ober_free_elements(scopedpdu);
439b89ba26fSmartijn 			goto fail;
440a99ea7faSmartijn 		}
441696b5899Stb 		if (ober_scanf_elements(message, "{SSe", &secparams) == -1)
4424f098f75Smartijn 			goto fail;
443696b5899Stb 		ober_set_writecallback(secparams, snmp_v3_secparamsoffset,
4444f098f75Smartijn 		    &secparamsoffset);
445e13f0058Smartijn 		break;
446e13f0058Smartijn 	}
447e13f0058Smartijn 
448696b5899Stb 	if (ober_write_elements(&ber, message) == -1)
449e13f0058Smartijn 		goto fail;
450e13f0058Smartijn 	ret = ber_copy_writebuf(&ber, (void **)&packet);
451e13f0058Smartijn 
452e13f0058Smartijn 	*len = (size_t) ret;
453696b5899Stb 	ober_free(&ber);
454e13f0058Smartijn 
4554f098f75Smartijn 	if (agent->version == SNMP_V3 && packet != NULL) {
4564f098f75Smartijn 		if (agent->v3->sec->finalparams(agent, packet,
4574f098f75Smartijn 		    ret, secparamsoffset, cookie) == -1) {
4584f098f75Smartijn 			free(packet);
4594f098f75Smartijn 			packet = NULL;
4604f098f75Smartijn 		}
4614f098f75Smartijn 	}
4624f098f75Smartijn 
463e13f0058Smartijn fail:
4644f098f75Smartijn 	if (agent->version == SNMP_V3)
4654f098f75Smartijn 		agent->v3->sec->freecookie(cookie);
466696b5899Stb 	ober_free_elements(message);
467b89ba26fSmartijn 	free(securityparams);
468e13f0058Smartijn 	return packet;
469e13f0058Smartijn }
470e13f0058Smartijn 
471e13f0058Smartijn static struct ber_element *
snmp_unpackage(struct snmp_agent * agent,char * buf,size_t buflen)472e13f0058Smartijn snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
473e13f0058Smartijn {
474e13f0058Smartijn 	struct ber ber;
475e13f0058Smartijn 	enum snmp_version version;
476e13f0058Smartijn 	char *community;
477e13f0058Smartijn 	struct ber_element *pdu;
478b89ba26fSmartijn 	long long msgid, model;
479b89ba26fSmartijn 	int msgsz;
480b89ba26fSmartijn 	char *msgflags, *secparams;
481b89ba26fSmartijn 	size_t msgflagslen, secparamslen;
482b89ba26fSmartijn 	struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
483b89ba26fSmartijn 	off_t secparamsoffset;
484f5e30c24Smartijn 	char *encpdu, *engineid;
485f5e30c24Smartijn 	size_t encpdulen, engineidlen;
486f5e30c24Smartijn 	void *cookie = NULL;
487e13f0058Smartijn 
488e13f0058Smartijn 	bzero(&ber, sizeof(ber));
489696b5899Stb 	ober_set_application(&ber, smi_application);
490e13f0058Smartijn 
491696b5899Stb 	ober_set_readbuf(&ber, buf, buflen);
492696b5899Stb 	if ((message = ober_read_elements(&ber, NULL)) == NULL)
493e13f0058Smartijn 		return NULL;
494696b5899Stb 	ober_free(&ber);
495e13f0058Smartijn 
496696b5899Stb 	if (ober_scanf_elements(message, "{de", &version, &payload) != 0)
497e13f0058Smartijn 		goto fail;
498e13f0058Smartijn 
499e13f0058Smartijn 	if (version != agent->version)
500e13f0058Smartijn 		goto fail;
501e13f0058Smartijn 
502b89ba26fSmartijn 	switch (version) {
503e13f0058Smartijn 	case SNMP_V1:
504e13f0058Smartijn 	case SNMP_V2C:
505696b5899Stb 		if (ober_scanf_elements(payload, "se", &community, &pdu) == -1)
506e13f0058Smartijn 			goto fail;
507e13f0058Smartijn 		if (strcmp(community, agent->community) != 0)
508e13f0058Smartijn 			goto fail;
509696b5899Stb 		ober_unlink_elements(payload);
510696b5899Stb 		ober_free_elements(message);
511e13f0058Smartijn 		return pdu;
512e13f0058Smartijn 	case SNMP_V3:
513696b5899Stb 		if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
514b89ba26fSmartijn 		    &msgflags, &msgflagslen, &model, &secparamsoffset,
515b89ba26fSmartijn 		    &secparams, &secparamslen, &scopedpdu) == -1)
516b89ba26fSmartijn 			goto fail;
517b89ba26fSmartijn 		if (msgflagslen != 1)
518b89ba26fSmartijn 			goto fail;
519b89ba26fSmartijn 		if (agent->v3->sec->parseparams(agent, buf, buflen,
520f5e30c24Smartijn 		    secparamsoffset, secparams, secparamslen, msgflags[0],
521f5e30c24Smartijn 		    &cookie) == -1) {
522f5e30c24Smartijn 			cookie = NULL;
523b89ba26fSmartijn 			goto fail;
524f5e30c24Smartijn 		}
525f5e30c24Smartijn 		if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
526696b5899Stb 			if (ober_scanf_elements(scopedpdu, "x", &encpdu,
527f5e30c24Smartijn 			    &encpdulen) == -1)
528f5e30c24Smartijn 				goto fail;
529f5e30c24Smartijn 			if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
530f5e30c24Smartijn 			    encpdulen, cookie)) == NULL)
531f5e30c24Smartijn 				goto fail;
532f5e30c24Smartijn 		}
533696b5899Stb 		if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid,
534b89ba26fSmartijn 		    &engineidlen, &ctxname) == -1)
535b89ba26fSmartijn 			goto fail;
536b89ba26fSmartijn 		if (!agent->v3->engineidset) {
537b89ba26fSmartijn 			if (snmp_v3_setengineid(agent->v3, engineid,
538b89ba26fSmartijn 			    engineidlen) == -1)
539b89ba26fSmartijn 				goto fail;
540b89ba26fSmartijn 		}
541696b5899Stb 		pdu = ober_unlink_elements(ctxname);
542b89ba26fSmartijn 		/* Accept reports, so we can continue if possible */
543b89ba26fSmartijn 		if (pdu->be_type != SNMP_C_REPORT) {
544b89ba26fSmartijn 			if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
545b89ba26fSmartijn 			    (agent->v3->level & SNMP_MSGFLAG_SECMASK))
546b89ba26fSmartijn 				goto fail;
547b89ba26fSmartijn 		}
548b89ba26fSmartijn 
549696b5899Stb 		ober_free_elements(message);
550f5e30c24Smartijn 		agent->v3->sec->freecookie(cookie);
551b89ba26fSmartijn 		return pdu;
552e13f0058Smartijn 	}
553e13f0058Smartijn 	/* NOTREACHED */
554e13f0058Smartijn 
555e13f0058Smartijn fail:
556f5e30c24Smartijn 	if (version == SNMP_V3)
557f5e30c24Smartijn 		agent->v3->sec->freecookie(cookie);
558696b5899Stb 	ober_free_elements(message);
559e13f0058Smartijn 	return NULL;
560e13f0058Smartijn }
561e13f0058Smartijn 
5624f098f75Smartijn static void
snmp_v3_secparamsoffset(void * cookie,size_t offset)5634f098f75Smartijn snmp_v3_secparamsoffset(void *cookie, size_t offset)
5644f098f75Smartijn {
5654f098f75Smartijn 	size_t *spoffset = cookie;
5664f098f75Smartijn 
5674f098f75Smartijn 	*spoffset = offset;
5684f098f75Smartijn }
5694f098f75Smartijn 
570e13f0058Smartijn ssize_t
ber_copy_writebuf(struct ber * ber,void ** buf)571e13f0058Smartijn ber_copy_writebuf(struct ber *ber, void **buf)
572e13f0058Smartijn {
573e13f0058Smartijn 	char *bbuf;
574e13f0058Smartijn 	ssize_t ret;
575e13f0058Smartijn 
576e13f0058Smartijn 	*buf = NULL;
577696b5899Stb 	if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1)
578e13f0058Smartijn 		return -1;
579e13f0058Smartijn 	if ((*buf = malloc(ret)) == NULL)
580e13f0058Smartijn 		return -1;
581e13f0058Smartijn 	memcpy(*buf, bbuf, ret);
582e13f0058Smartijn 	return  ret;
583e13f0058Smartijn }
584