xref: /openbsd-src/usr.bin/snmp/snmp.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: snmp.c,v 1.9 2019/10/24 12:39:26 tb Exp $	*/
2 
3 /*
4  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5  * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/socket.h>
21 
22 #include <errno.h>
23 #include <poll.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <time.h>
28 
29 #include "ber.h"
30 #include "smi.h"
31 #include "snmp.h"
32 
33 #define UDP_MAXPACKET 65535
34 
35 static struct ber_element *
36     snmp_resolve(struct snmp_agent *, struct ber_element *, int);
37 static char *
38     snmp_package(struct snmp_agent *, struct ber_element *, size_t *);
39 static struct ber_element *
40     snmp_unpackage(struct snmp_agent *, char *, size_t);
41 static void snmp_v3_free(struct snmp_v3 *);
42 static void snmp_v3_secparamsoffset(void *, size_t);
43 
44 struct snmp_v3 *
45 snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
46     struct snmp_sec *sec)
47 {
48 	struct snmp_v3 *v3;
49 
50 	if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level ||
51 	    sec == NULL) {
52 		errno = EINVAL;
53 		return NULL;
54 	}
55 	if ((v3 = calloc(1, sizeof(*v3))) == NULL)
56 		return NULL;
57 
58 	v3->level = level | SNMP_MSGFLAG_REPORT;
59 	v3->ctxnamelen = ctxnamelen;
60 	if (ctxnamelen != 0) {
61 		if ((v3->ctxname = malloc(ctxnamelen)) == NULL) {
62 			free(v3);
63 			return NULL;
64 		}
65 		memcpy(v3->ctxname, ctxname, ctxnamelen);
66 	}
67 	v3->sec = sec;
68 	return v3;
69 }
70 
71 int
72 snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen)
73 {
74 	if (v3->engineidset)
75 		free(v3->engineid);
76 	if ((v3->engineid = malloc(engineidlen)) == NULL)
77 		return -1;
78 	memcpy(v3->engineid, engineid, engineidlen);
79 	v3->engineidlen = engineidlen;
80 	v3->engineidset = 1;
81 	return 0;
82 }
83 
84 struct snmp_agent *
85 snmp_connect_v12(int fd, enum snmp_version version, const char *community)
86 {
87 	struct snmp_agent *agent;
88 
89 	if (version != SNMP_V1 && version != SNMP_V2C) {
90 		errno = EINVAL;
91 		return NULL;
92 	}
93 	if ((agent = malloc(sizeof(*agent))) == NULL)
94 		return NULL;
95 	agent->fd = fd;
96 	agent->version = version;
97 	if ((agent->community = strdup(community)) == NULL)
98 		goto fail;
99 	agent->timeout = 1;
100 	agent->retries = 5;
101 	agent->v3 = NULL;
102 	return agent;
103 
104 fail:
105 	free(agent);
106 	return NULL;
107 }
108 
109 struct snmp_agent *
110 snmp_connect_v3(int fd, struct snmp_v3 *v3)
111 {
112 	struct snmp_agent *agent;
113 
114 	if ((agent = malloc(sizeof(*agent))) == NULL)
115 		return NULL;
116 	agent->fd = fd;
117 	agent->version = SNMP_V3;
118 	agent->v3 = v3;
119 	agent->timeout = 1;
120 	agent->retries = 5;
121 	agent->community = NULL;
122 
123 	if (v3->sec->init(agent) == -1) {
124 		snmp_free_agent(agent);
125 		return NULL;
126 	}
127 	return agent;
128 }
129 
130 void
131 snmp_free_agent(struct snmp_agent *agent)
132 {
133 	free(agent->community);
134 	if (agent->v3 != NULL)
135 		snmp_v3_free(agent->v3);
136 	free(agent);
137 }
138 
139 static void
140 snmp_v3_free(struct snmp_v3 *v3)
141 {
142 	v3->sec->free(v3->sec->data);
143 	free(v3->sec);
144 	free(v3->ctxname);
145 	free(v3->engineid);
146 	free(v3);
147 }
148 
149 struct ber_element *
150 snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
151 {
152 	struct ber_element *pdu, *varbind;
153 	size_t i;
154 
155 	if ((pdu = ober_add_sequence(NULL)) == NULL)
156 		return NULL;
157 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
158 	    SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
159 		goto fail;
160 	for (i = 0; i < len; i++)
161 		varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
162 		if (varbind == NULL)
163 			goto fail;
164 
165 	return snmp_resolve(agent, pdu, 1);
166 fail:
167 	ober_free_elements(pdu);
168 	return NULL;
169 }
170 
171 struct ber_element *
172 snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
173 {
174 	struct ber_element *pdu, *varbind;
175 	size_t i;
176 
177 	if ((pdu = ober_add_sequence(NULL)) == NULL)
178 		return NULL;
179 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
180 	    SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
181 		goto fail;
182 	for (i = 0; i < len; i++)
183 		varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
184 		if (varbind == NULL)
185 			goto fail;
186 
187 	return snmp_resolve(agent, pdu, 1);
188 fail:
189 	ober_free_elements(pdu);
190 	return NULL;
191 }
192 
193 int
194 snmp_trap(struct snmp_agent *agent, struct timespec *uptime,
195     struct ber_oid *oid, struct ber_element *custvarbind)
196 {
197 	struct ber_element *pdu, *varbind;
198 	struct ber_oid sysuptime, trap;
199 	long long ticks;
200 
201 	if ((pdu = ober_add_sequence(NULL)) == NULL)
202 		return -1;
203 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
204 	    SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL)
205 		goto fail;
206 
207 	ticks = uptime->tv_sec * 100;
208 	ticks += uptime->tv_nsec / 10000000;
209 	if (smi_string2oid("sysUpTime.0", &sysuptime) == -1)
210 		goto fail;
211 	if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
212 	    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
213 		goto fail;
214 	if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
215 		goto fail;
216 	if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
217 		goto fail;
218 	if (custvarbind != NULL)
219 		ober_link_elements(varbind, custvarbind);
220 
221 	snmp_resolve(agent, pdu, 0);
222 	return 0;
223 fail:
224 	ober_free_elements(pdu);
225 	return -1;
226 }
227 
228 struct ber_element *
229 snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len,
230     int non_repeaters, int max_repetitions)
231 {
232 	struct ber_element *pdu, *varbind;
233 	size_t i;
234 
235 	if ((pdu = ober_add_sequence(NULL)) == NULL)
236 		return NULL;
237 	if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
238 	    SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
239 	    max_repetitions)) == NULL)
240 		goto fail;
241 	for (i = 0; i < len; i++)
242 		varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
243 		if (varbind == NULL)
244 			goto fail;
245 
246 	return snmp_resolve(agent, pdu, 1);
247 fail:
248 	ober_free_elements(pdu);
249 	return NULL;
250 }
251 
252 struct ber_element *
253 snmp_set(struct snmp_agent *agent, struct ber_element *vblist)
254 {
255 	struct ber_element *pdu;
256 
257 	if ((pdu = ober_add_sequence(NULL)) == NULL)
258 		return NULL;
259 	if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT,
260 	    SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) {
261 		ober_free_elements(pdu);
262 		ober_free_elements(vblist);
263 		return NULL;
264 	}
265 
266 	return snmp_resolve(agent, pdu, 1);
267 }
268 
269 static struct ber_element *
270 snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
271 {
272 	struct ber_element *varbind;
273 	struct ber_oid oid;
274 	struct timespec start, now;
275 	struct pollfd pfd;
276 	char *message;
277 	ssize_t len;
278 	long long reqid, rreqid;
279 	short direction;
280 	int to, nfds, ret;
281 	int tries;
282 	char buf[READ_BUF_SIZE];
283 
284 	if (ober_scanf_elements(pdu, "{i", &reqid) != 0) {
285 		errno = EINVAL;
286 		ober_free_elements(pdu);
287 		return NULL;
288 	}
289 
290 	if ((message = snmp_package(agent, pdu, &len)) == NULL)
291 		return NULL;
292 
293 	clock_gettime(CLOCK_MONOTONIC, &start);
294 	memcpy(&now, &start, sizeof(now));
295 	direction = POLLOUT;
296 	tries = agent->retries + 1;
297 	while (tries) {
298 		pfd.fd = agent->fd;
299 		pfd.events = direction;
300 		if (agent->timeout > 0) {
301 			to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000;
302 			to -= (now.tv_nsec - start.tv_nsec) / 1000000;
303 		} else
304 			to = INFTIM;
305 		nfds = poll(&pfd, 1, to);
306 		if (nfds == 0) {
307 			errno = ETIMEDOUT;
308 			direction = POLLOUT;
309 			tries--;
310 			continue;
311 		}
312 		if (nfds == -1) {
313 			if (errno == EINTR)
314 				continue;
315 			else
316 				goto fail;
317 		}
318 		if (direction == POLLOUT) {
319 			ret = send(agent->fd, message, len, MSG_DONTWAIT);
320 			if (ret == -1)
321 				goto fail;
322 			if (ret < len) {
323 				errno = EBADMSG;
324 				goto fail;
325 			}
326 			if (!reply)
327 				return NULL;
328 			direction = POLLIN;
329 			continue;
330 		}
331 		ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT);
332 		if (ret == 0)
333 			errno = ECONNRESET;
334 		if (ret <= 0)
335 			goto fail;
336 		if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
337 			tries--;
338 			direction = POLLOUT;
339 			errno = EPROTO;
340 			continue;
341 		}
342 		/* Validate pdu format and check request id */
343 		if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
344 		    varbind->be_encoding != BER_TYPE_SEQUENCE) {
345 			errno = EPROTO;
346 			direction = POLLOUT;
347 			tries--;
348 			continue;
349 		}
350 		if (rreqid != reqid && rreqid != 0) {
351 			errno = EPROTO;
352 			direction = POLLOUT;
353 			tries--;
354 			continue;
355 		}
356 		for (varbind = varbind->be_sub; varbind != NULL;
357 		    varbind = varbind->be_next) {
358 			if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) {
359 				errno = EPROTO;
360 				direction = POLLOUT;
361 				tries--;
362 				continue;
363 			}
364 		}
365 
366 		free(message);
367 		return pdu;
368 	}
369 
370 fail:
371 	free(message);
372 	return NULL;
373 }
374 
375 static char *
376 snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
377 {
378 	struct ber ber;
379 	struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
380 	ssize_t securitysize, ret;
381 	size_t secparamsoffset;
382 	char *securityparams = NULL, *packet = NULL;
383 	long long msgid;
384 	void *cookie = NULL;
385 
386 	bzero(&ber, sizeof(ber));
387 	ober_set_application(&ber, smi_application);
388 
389 	if ((message = ober_add_sequence(NULL)) == NULL) {
390 		ober_free_elements(pdu);
391 		goto fail;
392 	}
393 
394 	switch (agent->version) {
395 	case SNMP_V1:
396 	case SNMP_V2C:
397 		if (ober_printf_elements(message, "dse", agent->version,
398 		    agent->community, pdu) == NULL) {
399 			ober_free_elements(pdu);
400 			goto fail;
401 		}
402 		break;
403 	case SNMP_V3:
404 		msgid = arc4random_uniform(2147483647);
405 		if ((scopedpdu = ober_add_sequence(NULL)) == NULL) {
406 			ober_free_elements(pdu);
407 			goto fail;
408 		}
409 		if (ober_printf_elements(scopedpdu, "xxe",
410 		    agent->v3->engineid, agent->v3->engineidlen,
411 		    agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
412 			ober_free_elements(pdu);
413 			ober_free_elements(scopedpdu);
414 			goto fail;
415 		}
416 		pdu = NULL;
417 		if ((securityparams = agent->v3->sec->genparams(agent,
418 		    &securitysize, &cookie)) == NULL) {
419 			ober_free_elements(scopedpdu);
420 			goto fail;
421 		}
422 		if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
423 			if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
424 			    cookie)) == NULL)
425 				goto fail;
426 			ober_free_elements(scopedpdu);
427 			scopedpdu = encpdu;
428 		}
429 		if (ober_printf_elements(message, "d{idxd}xe",
430 		    agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
431 		    (size_t) 1, agent->v3->sec->model, securityparams,
432 		    securitysize, scopedpdu) == NULL) {
433 			ober_free_elements(scopedpdu);
434 			goto fail;
435 		}
436 		if (ober_scanf_elements(message, "{SSe", &secparams) == -1)
437 			goto fail;
438 		ober_set_writecallback(secparams, snmp_v3_secparamsoffset,
439 		    &secparamsoffset);
440 		break;
441 	}
442 
443 	if (ober_write_elements(&ber, message) == -1)
444 		goto fail;
445 	ret = ber_copy_writebuf(&ber, (void **)&packet);
446 
447 	*len = (size_t) ret;
448 	ober_free(&ber);
449 
450 	if (agent->version == SNMP_V3 && packet != NULL) {
451 		if (agent->v3->sec->finalparams(agent, packet,
452 		    ret, secparamsoffset, cookie) == -1) {
453 			free(packet);
454 			packet = NULL;
455 		}
456 	}
457 
458 fail:
459 	if (agent->version == SNMP_V3)
460 		agent->v3->sec->freecookie(cookie);
461 	ober_free_elements(message);
462 	free(securityparams);
463 	return packet;
464 }
465 
466 static struct ber_element *
467 snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
468 {
469 	struct ber ber;
470 	enum snmp_version version;
471 	char *community;
472 	struct ber_element *pdu;
473 	long long msgid, model;
474 	int msgsz;
475 	char *msgflags, *secparams;
476 	size_t msgflagslen, secparamslen;
477 	struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
478 	off_t secparamsoffset;
479 	char *encpdu, *engineid;
480 	size_t encpdulen, engineidlen;
481 	void *cookie = NULL;
482 
483 	bzero(&ber, sizeof(ber));
484 	ober_set_application(&ber, smi_application);
485 
486 	ober_set_readbuf(&ber, buf, buflen);
487 	if ((message = ober_read_elements(&ber, NULL)) == NULL)
488 		return NULL;
489 	ober_free(&ber);
490 
491 	if (ober_scanf_elements(message, "{de", &version, &payload) != 0)
492 		goto fail;
493 
494 	if (version != agent->version)
495 		goto fail;
496 
497 	switch (version) {
498 	case SNMP_V1:
499 	case SNMP_V2C:
500 		if (ober_scanf_elements(payload, "se", &community, &pdu) == -1)
501 			goto fail;
502 		if (strcmp(community, agent->community) != 0)
503 			goto fail;
504 		ober_unlink_elements(payload);
505 		ober_free_elements(message);
506 		return pdu;
507 	case SNMP_V3:
508 		if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
509 		    &msgflags, &msgflagslen, &model, &secparamsoffset,
510 		    &secparams, &secparamslen, &scopedpdu) == -1)
511 			goto fail;
512 		if (msgflagslen != 1)
513 			goto fail;
514 		if (agent->v3->sec->parseparams(agent, buf, buflen,
515 		    secparamsoffset, secparams, secparamslen, msgflags[0],
516 		    &cookie) == -1) {
517 			cookie = NULL;
518 			goto fail;
519 		}
520 		if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
521 			if (ober_scanf_elements(scopedpdu, "x", &encpdu,
522 			    &encpdulen) == -1)
523 				goto fail;
524 			if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
525 			    encpdulen, cookie)) == NULL)
526 				goto fail;
527 		}
528 		if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid,
529 		    &engineidlen, &ctxname) == -1)
530 			goto fail;
531 		if (!agent->v3->engineidset) {
532 			if (snmp_v3_setengineid(agent->v3, engineid,
533 			    engineidlen) == -1)
534 				goto fail;
535 		}
536 		pdu = ober_unlink_elements(ctxname);
537 		/* Accept reports, so we can continue if possible */
538 		if (pdu->be_type != SNMP_C_REPORT) {
539 			if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
540 			    (agent->v3->level & SNMP_MSGFLAG_SECMASK))
541 				goto fail;
542 		}
543 
544 		ober_free_elements(message);
545 		agent->v3->sec->freecookie(cookie);
546 		return pdu;
547 	}
548 	/* NOTREACHED */
549 
550 fail:
551 	if (version == SNMP_V3)
552 		agent->v3->sec->freecookie(cookie);
553 	ober_free_elements(message);
554 	return NULL;
555 }
556 
557 static void
558 snmp_v3_secparamsoffset(void *cookie, size_t offset)
559 {
560 	size_t *spoffset = cookie;
561 
562 	*spoffset = offset;
563 }
564 
565 ssize_t
566 ber_copy_writebuf(struct ber *ber, void **buf)
567 {
568 	char *bbuf;
569 	ssize_t ret;
570 
571 	*buf = NULL;
572 	if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1)
573 		return -1;
574 	if ((*buf = malloc(ret)) == NULL)
575 		return -1;
576 	memcpy(*buf, bbuf, ret);
577 	return  ret;
578 }
579