Annotation of src/usr.bin/snmp/snmp.c, Revision 1.11
1.11 ! deraadt 1: /* $OpenBSD: snmp.c,v 1.10 2020/03/24 14:09:14 martijn Exp $ */
1.1 martijn 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:
1.4 martijn 33: #define UDP_MAXPACKET 65535
34:
1.1 martijn 35: static struct ber_element *
36: snmp_resolve(struct snmp_agent *, struct ber_element *, int);
1.3 martijn 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);
1.4 martijn 41: static void snmp_v3_free(struct snmp_v3 *);
1.5 martijn 42: static void snmp_v3_secparamsoffset(void *, size_t);
1.4 martijn 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: }
1.1 martijn 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;
1.4 martijn 101: agent->v3 = NULL;
1.1 martijn 102: return agent;
103:
104: fail:
105: free(agent);
106: return NULL;
107: }
108:
1.4 martijn 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:
1.1 martijn 130: void
131: snmp_free_agent(struct snmp_agent *agent)
132: {
133: free(agent->community);
1.4 martijn 134: if (agent->v3 != NULL)
135: snmp_v3_free(agent->v3);
1.1 martijn 136: free(agent);
137: }
138:
1.4 martijn 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:
1.1 martijn 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:
1.9 tb 155: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 156: return NULL;
1.9 tb 157: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 158: SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
159: goto fail;
1.11 ! deraadt 160: for (i = 0; i < len; i++) {
1.9 tb 161: varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
1.1 martijn 162: if (varbind == NULL)
163: goto fail;
1.11 ! deraadt 164: }
1.1 martijn 165:
166: return snmp_resolve(agent, pdu, 1);
167: fail:
1.9 tb 168: ober_free_elements(pdu);
1.1 martijn 169: return NULL;
170: }
171:
172: struct ber_element *
173: snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
174: {
175: struct ber_element *pdu, *varbind;
176: size_t i;
177:
1.9 tb 178: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 179: return NULL;
1.9 tb 180: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 181: SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
182: goto fail;
1.11 ! deraadt 183: for (i = 0; i < len; i++) {
1.9 tb 184: varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
1.1 martijn 185: if (varbind == NULL)
186: goto fail;
1.11 ! deraadt 187: }
1.1 martijn 188:
189: return snmp_resolve(agent, pdu, 1);
190: fail:
1.9 tb 191: ober_free_elements(pdu);
1.1 martijn 192: return NULL;
193: }
194:
195: int
196: snmp_trap(struct snmp_agent *agent, struct timespec *uptime,
197: struct ber_oid *oid, struct ber_element *custvarbind)
198: {
199: struct ber_element *pdu, *varbind;
200: struct ber_oid sysuptime, trap;
201: long long ticks;
202:
1.9 tb 203: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 204: return -1;
1.9 tb 205: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 206: SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL)
207: goto fail;
208:
209: ticks = uptime->tv_sec * 100;
210: ticks += uptime->tv_nsec / 10000000;
211: if (smi_string2oid("sysUpTime.0", &sysuptime) == -1)
212: goto fail;
1.9 tb 213: if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
1.1 martijn 214: BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
215: goto fail;
216: if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
217: goto fail;
1.9 tb 218: if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
1.1 martijn 219: goto fail;
220: if (custvarbind != NULL)
1.9 tb 221: ober_link_elements(varbind, custvarbind);
1.1 martijn 222:
223: snmp_resolve(agent, pdu, 0);
224: return 0;
225: fail:
1.9 tb 226: ober_free_elements(pdu);
1.1 martijn 227: return -1;
228: }
229:
230: struct ber_element *
231: snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len,
232: int non_repeaters, int max_repetitions)
233: {
234: struct ber_element *pdu, *varbind;
235: size_t i;
236:
1.9 tb 237: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 238: return NULL;
1.9 tb 239: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 240: SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
241: max_repetitions)) == NULL)
242: goto fail;
1.11 ! deraadt 243: for (i = 0; i < len; i++) {
1.9 tb 244: varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
1.1 martijn 245: if (varbind == NULL)
246: goto fail;
1.11 ! deraadt 247: }
1.1 martijn 248:
249: return snmp_resolve(agent, pdu, 1);
250: fail:
1.9 tb 251: ober_free_elements(pdu);
1.1 martijn 252: return NULL;
1.7 martijn 253: }
254:
255: struct ber_element *
256: snmp_set(struct snmp_agent *agent, struct ber_element *vblist)
257: {
258: struct ber_element *pdu;
259:
1.9 tb 260: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.7 martijn 261: return NULL;
1.9 tb 262: if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT,
1.7 martijn 263: SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) {
1.9 tb 264: ober_free_elements(pdu);
265: ober_free_elements(vblist);
1.7 martijn 266: return NULL;
267: }
268:
269: return snmp_resolve(agent, pdu, 1);
1.1 martijn 270: }
271:
272: static struct ber_element *
273: snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
274: {
1.3 martijn 275: struct ber_element *varbind;
1.1 martijn 276: struct ber_oid oid;
277: struct timespec start, now;
278: struct pollfd pfd;
1.3 martijn 279: char *message;
1.1 martijn 280: ssize_t len;
281: long long reqid, rreqid;
282: short direction;
283: int to, nfds, ret;
284: int tries;
285: char buf[READ_BUF_SIZE];
286:
1.9 tb 287: if (ober_scanf_elements(pdu, "{i", &reqid) != 0) {
1.1 martijn 288: errno = EINVAL;
1.9 tb 289: ober_free_elements(pdu);
1.1 martijn 290: return NULL;
291: }
292:
1.3 martijn 293: if ((message = snmp_package(agent, pdu, &len)) == NULL)
1.1 martijn 294: return NULL;
295:
296: clock_gettime(CLOCK_MONOTONIC, &start);
297: memcpy(&now, &start, sizeof(now));
298: direction = POLLOUT;
299: tries = agent->retries + 1;
300: while (tries) {
301: pfd.fd = agent->fd;
302: pfd.events = direction;
303: if (agent->timeout > 0) {
304: to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000;
305: to -= (now.tv_nsec - start.tv_nsec) / 1000000;
306: } else
307: to = INFTIM;
308: nfds = poll(&pfd, 1, to);
309: if (nfds == 0) {
310: errno = ETIMEDOUT;
311: direction = POLLOUT;
312: tries--;
313: continue;
314: }
315: if (nfds == -1) {
316: if (errno == EINTR)
317: continue;
318: else
319: goto fail;
320: }
321: if (direction == POLLOUT) {
1.3 martijn 322: ret = send(agent->fd, message, len, MSG_DONTWAIT);
1.1 martijn 323: if (ret == -1)
324: goto fail;
325: if (ret < len) {
326: errno = EBADMSG;
327: goto fail;
328: }
329: if (!reply)
330: return NULL;
331: direction = POLLIN;
332: continue;
333: }
334: ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT);
335: if (ret == 0)
336: errno = ECONNRESET;
337: if (ret <= 0)
338: goto fail;
1.3 martijn 339: if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
1.2 martijn 340: tries--;
341: direction = POLLOUT;
342: errno = EPROTO;
1.1 martijn 343: continue;
1.2 martijn 344: }
1.1 martijn 345: /* Validate pdu format and check request id */
1.9 tb 346: if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
1.2 martijn 347: varbind->be_encoding != BER_TYPE_SEQUENCE) {
348: errno = EPROTO;
349: direction = POLLOUT;
350: tries--;
351: continue;
352: }
1.4 martijn 353: if (rreqid != reqid && rreqid != 0) {
1.2 martijn 354: errno = EPROTO;
355: direction = POLLOUT;
356: tries--;
1.1 martijn 357: continue;
1.2 martijn 358: }
1.1 martijn 359: for (varbind = varbind->be_sub; varbind != NULL;
360: varbind = varbind->be_next) {
1.9 tb 361: if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) {
1.2 martijn 362: errno = EPROTO;
363: direction = POLLOUT;
364: tries--;
1.10 martijn 365: break;
1.2 martijn 366: }
1.1 martijn 367: }
1.10 martijn 368: if (varbind != NULL)
369: continue;
1.1 martijn 370:
1.3 martijn 371: free(message);
372: return pdu;
373: }
374:
375: fail:
376: free(message);
377: return NULL;
378: }
379:
380: static char *
381: snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
382: {
383: struct ber ber;
1.6 martijn 384: struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
1.4 martijn 385: ssize_t securitysize, ret;
1.5 martijn 386: size_t secparamsoffset;
1.4 martijn 387: char *securityparams = NULL, *packet = NULL;
388: long long msgid;
1.5 martijn 389: void *cookie = NULL;
1.3 martijn 390:
391: bzero(&ber, sizeof(ber));
1.9 tb 392: ober_set_application(&ber, smi_application);
1.3 martijn 393:
1.9 tb 394: if ((message = ober_add_sequence(NULL)) == NULL) {
395: ober_free_elements(pdu);
1.3 martijn 396: goto fail;
397: }
398:
399: switch (agent->version) {
400: case SNMP_V1:
401: case SNMP_V2C:
1.9 tb 402: if (ober_printf_elements(message, "dse", agent->version,
1.3 martijn 403: agent->community, pdu) == NULL) {
1.9 tb 404: ober_free_elements(pdu);
1.3 martijn 405: goto fail;
406: }
407: break;
408: case SNMP_V3:
1.4 martijn 409: msgid = arc4random_uniform(2147483647);
1.9 tb 410: if ((scopedpdu = ober_add_sequence(NULL)) == NULL) {
411: ober_free_elements(pdu);
1.4 martijn 412: goto fail;
413: }
1.9 tb 414: if (ober_printf_elements(scopedpdu, "xxe",
1.4 martijn 415: agent->v3->engineid, agent->v3->engineidlen,
416: agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
1.9 tb 417: ober_free_elements(pdu);
418: ober_free_elements(scopedpdu);
1.4 martijn 419: goto fail;
420: }
421: pdu = NULL;
422: if ((securityparams = agent->v3->sec->genparams(agent,
1.5 martijn 423: &securitysize, &cookie)) == NULL) {
1.9 tb 424: ober_free_elements(scopedpdu);
1.4 martijn 425: goto fail;
426: }
1.6 martijn 427: if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
428: if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
429: cookie)) == NULL)
430: goto fail;
1.9 tb 431: ober_free_elements(scopedpdu);
1.6 martijn 432: scopedpdu = encpdu;
433: }
1.9 tb 434: if (ober_printf_elements(message, "d{idxd}xe",
1.4 martijn 435: agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
436: (size_t) 1, agent->v3->sec->model, securityparams,
1.8 martijn 437: securitysize, scopedpdu) == NULL) {
1.9 tb 438: ober_free_elements(scopedpdu);
1.4 martijn 439: goto fail;
1.8 martijn 440: }
1.9 tb 441: if (ober_scanf_elements(message, "{SSe", &secparams) == -1)
1.5 martijn 442: goto fail;
1.9 tb 443: ober_set_writecallback(secparams, snmp_v3_secparamsoffset,
1.5 martijn 444: &secparamsoffset);
1.3 martijn 445: break;
446: }
447:
1.9 tb 448: if (ober_write_elements(&ber, message) == -1)
1.3 martijn 449: goto fail;
450: ret = ber_copy_writebuf(&ber, (void **)&packet);
451:
452: *len = (size_t) ret;
1.9 tb 453: ober_free(&ber);
1.3 martijn 454:
1.5 martijn 455: if (agent->version == SNMP_V3 && packet != NULL) {
456: if (agent->v3->sec->finalparams(agent, packet,
457: ret, secparamsoffset, cookie) == -1) {
458: free(packet);
459: packet = NULL;
460: }
461: }
462:
1.3 martijn 463: fail:
1.5 martijn 464: if (agent->version == SNMP_V3)
465: agent->v3->sec->freecookie(cookie);
1.9 tb 466: ober_free_elements(message);
1.4 martijn 467: free(securityparams);
1.3 martijn 468: return packet;
469: }
470:
471: static struct ber_element *
472: snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
473: {
474: struct ber ber;
475: enum snmp_version version;
476: char *community;
477: struct ber_element *pdu;
1.4 martijn 478: long long msgid, model;
479: int msgsz;
480: char *msgflags, *secparams;
481: size_t msgflagslen, secparamslen;
482: struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
483: off_t secparamsoffset;
1.6 martijn 484: char *encpdu, *engineid;
485: size_t encpdulen, engineidlen;
486: void *cookie = NULL;
1.3 martijn 487:
488: bzero(&ber, sizeof(ber));
1.9 tb 489: ober_set_application(&ber, smi_application);
1.3 martijn 490:
1.9 tb 491: ober_set_readbuf(&ber, buf, buflen);
492: if ((message = ober_read_elements(&ber, NULL)) == NULL)
1.3 martijn 493: return NULL;
1.9 tb 494: ober_free(&ber);
1.3 martijn 495:
1.9 tb 496: if (ober_scanf_elements(message, "{de", &version, &payload) != 0)
1.3 martijn 497: goto fail;
498:
499: if (version != agent->version)
500: goto fail;
501:
1.4 martijn 502: switch (version) {
1.3 martijn 503: case SNMP_V1:
504: case SNMP_V2C:
1.9 tb 505: if (ober_scanf_elements(payload, "se", &community, &pdu) == -1)
1.3 martijn 506: goto fail;
507: if (strcmp(community, agent->community) != 0)
508: goto fail;
1.9 tb 509: ober_unlink_elements(payload);
510: ober_free_elements(message);
1.1 martijn 511: return pdu;
1.3 martijn 512: case SNMP_V3:
1.9 tb 513: if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
1.4 martijn 514: &msgflags, &msgflagslen, &model, &secparamsoffset,
515: &secparams, &secparamslen, &scopedpdu) == -1)
516: goto fail;
517: if (msgflagslen != 1)
518: goto fail;
519: if (agent->v3->sec->parseparams(agent, buf, buflen,
1.6 martijn 520: secparamsoffset, secparams, secparamslen, msgflags[0],
521: &cookie) == -1) {
522: cookie = NULL;
1.4 martijn 523: goto fail;
1.6 martijn 524: }
525: if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
1.9 tb 526: if (ober_scanf_elements(scopedpdu, "x", &encpdu,
1.6 martijn 527: &encpdulen) == -1)
528: goto fail;
529: if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
530: encpdulen, cookie)) == NULL)
531: goto fail;
532: }
1.9 tb 533: if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid,
1.4 martijn 534: &engineidlen, &ctxname) == -1)
535: goto fail;
536: if (!agent->v3->engineidset) {
537: if (snmp_v3_setengineid(agent->v3, engineid,
538: engineidlen) == -1)
539: goto fail;
540: }
1.9 tb 541: pdu = ober_unlink_elements(ctxname);
1.4 martijn 542: /* Accept reports, so we can continue if possible */
543: if (pdu->be_type != SNMP_C_REPORT) {
544: if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
545: (agent->v3->level & SNMP_MSGFLAG_SECMASK))
546: goto fail;
547: }
548:
1.9 tb 549: ober_free_elements(message);
1.6 martijn 550: agent->v3->sec->freecookie(cookie);
1.4 martijn 551: return pdu;
1.1 martijn 552: }
1.3 martijn 553: /* NOTREACHED */
1.1 martijn 554:
555: fail:
1.6 martijn 556: if (version == SNMP_V3)
557: agent->v3->sec->freecookie(cookie);
1.9 tb 558: ober_free_elements(message);
1.1 martijn 559: return NULL;
1.5 martijn 560: }
561:
562: static void
563: snmp_v3_secparamsoffset(void *cookie, size_t offset)
564: {
565: size_t *spoffset = cookie;
566:
567: *spoffset = offset;
1.3 martijn 568: }
569:
570: ssize_t
571: ber_copy_writebuf(struct ber *ber, void **buf)
572: {
573: char *bbuf;
574: ssize_t ret;
575:
576: *buf = NULL;
1.9 tb 577: if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1)
1.3 martijn 578: return -1;
579: if ((*buf = malloc(ret)) == NULL)
580: return -1;
581: memcpy(*buf, bbuf, ret);
582: return ret;
1.1 martijn 583: }