Annotation of src/usr.bin/snmp/snmp.c, Revision 1.8.2.1
1.8.2.1 ! tb 1: /* $OpenBSD: snmp.c,v 1.8 2019/10/08 10:00:42 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.8.2.1 ! tb 155: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 156: return NULL;
1.8.2.1 ! 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;
160: for (i = 0; i < len; i++)
1.8.2.1 ! tb 161: varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
1.1 martijn 162: if (varbind == NULL)
163: goto fail;
164:
165: return snmp_resolve(agent, pdu, 1);
166: fail:
1.8.2.1 ! tb 167: ober_free_elements(pdu);
1.1 martijn 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:
1.8.2.1 ! tb 177: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 178: return NULL;
1.8.2.1 ! tb 179: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 180: SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
181: goto fail;
182: for (i = 0; i < len; i++)
1.8.2.1 ! tb 183: varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
1.1 martijn 184: if (varbind == NULL)
185: goto fail;
186:
187: return snmp_resolve(agent, pdu, 1);
188: fail:
1.8.2.1 ! tb 189: ober_free_elements(pdu);
1.1 martijn 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:
1.8.2.1 ! tb 201: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 202: return -1;
1.8.2.1 ! tb 203: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 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;
1.8.2.1 ! tb 211: if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
1.1 martijn 212: BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
213: goto fail;
214: if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
215: goto fail;
1.8.2.1 ! tb 216: if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
1.1 martijn 217: goto fail;
218: if (custvarbind != NULL)
1.8.2.1 ! tb 219: ober_link_elements(varbind, custvarbind);
1.1 martijn 220:
221: snmp_resolve(agent, pdu, 0);
222: return 0;
223: fail:
1.8.2.1 ! tb 224: ober_free_elements(pdu);
1.1 martijn 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:
1.8.2.1 ! tb 235: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 236: return NULL;
1.8.2.1 ! tb 237: if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
1.1 martijn 238: SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
239: max_repetitions)) == NULL)
240: goto fail;
241: for (i = 0; i < len; i++)
1.8.2.1 ! tb 242: varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
1.1 martijn 243: if (varbind == NULL)
244: goto fail;
245:
246: return snmp_resolve(agent, pdu, 1);
247: fail:
1.8.2.1 ! tb 248: ober_free_elements(pdu);
1.1 martijn 249: return NULL;
1.7 martijn 250: }
251:
252: struct ber_element *
253: snmp_set(struct snmp_agent *agent, struct ber_element *vblist)
254: {
255: struct ber_element *pdu;
256:
1.8.2.1 ! tb 257: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.7 martijn 258: return NULL;
1.8.2.1 ! tb 259: if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT,
1.7 martijn 260: SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) {
1.8.2.1 ! tb 261: ober_free_elements(pdu);
! 262: ober_free_elements(vblist);
1.7 martijn 263: return NULL;
264: }
265:
266: return snmp_resolve(agent, pdu, 1);
1.1 martijn 267: }
268:
269: static struct ber_element *
270: snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
271: {
1.3 martijn 272: struct ber_element *varbind;
1.1 martijn 273: struct ber_oid oid;
274: struct timespec start, now;
275: struct pollfd pfd;
1.3 martijn 276: char *message;
1.1 martijn 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:
1.8.2.1 ! tb 284: if (ober_scanf_elements(pdu, "{i", &reqid) != 0) {
1.1 martijn 285: errno = EINVAL;
1.8.2.1 ! tb 286: ober_free_elements(pdu);
1.1 martijn 287: return NULL;
288: }
289:
1.3 martijn 290: if ((message = snmp_package(agent, pdu, &len)) == NULL)
1.1 martijn 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) {
1.3 martijn 319: ret = send(agent->fd, message, len, MSG_DONTWAIT);
1.1 martijn 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;
1.3 martijn 336: if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
1.2 martijn 337: tries--;
338: direction = POLLOUT;
339: errno = EPROTO;
1.1 martijn 340: continue;
1.2 martijn 341: }
1.1 martijn 342: /* Validate pdu format and check request id */
1.8.2.1 ! tb 343: if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
1.2 martijn 344: varbind->be_encoding != BER_TYPE_SEQUENCE) {
345: errno = EPROTO;
346: direction = POLLOUT;
347: tries--;
348: continue;
349: }
1.4 martijn 350: if (rreqid != reqid && rreqid != 0) {
1.2 martijn 351: errno = EPROTO;
352: direction = POLLOUT;
353: tries--;
1.1 martijn 354: continue;
1.2 martijn 355: }
1.1 martijn 356: for (varbind = varbind->be_sub; varbind != NULL;
357: varbind = varbind->be_next) {
1.8.2.1 ! tb 358: if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) {
1.2 martijn 359: errno = EPROTO;
360: direction = POLLOUT;
361: tries--;
1.4 martijn 362: continue;
1.2 martijn 363: }
1.1 martijn 364: }
365:
1.3 martijn 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;
1.6 martijn 379: struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
1.4 martijn 380: ssize_t securitysize, ret;
1.5 martijn 381: size_t secparamsoffset;
1.4 martijn 382: char *securityparams = NULL, *packet = NULL;
383: long long msgid;
1.5 martijn 384: void *cookie = NULL;
1.3 martijn 385:
386: bzero(&ber, sizeof(ber));
1.8.2.1 ! tb 387: ober_set_application(&ber, smi_application);
1.3 martijn 388:
1.8.2.1 ! tb 389: if ((message = ober_add_sequence(NULL)) == NULL) {
! 390: ober_free_elements(pdu);
1.3 martijn 391: goto fail;
392: }
393:
394: switch (agent->version) {
395: case SNMP_V1:
396: case SNMP_V2C:
1.8.2.1 ! tb 397: if (ober_printf_elements(message, "dse", agent->version,
1.3 martijn 398: agent->community, pdu) == NULL) {
1.8.2.1 ! tb 399: ober_free_elements(pdu);
1.3 martijn 400: goto fail;
401: }
402: break;
403: case SNMP_V3:
1.4 martijn 404: msgid = arc4random_uniform(2147483647);
1.8.2.1 ! tb 405: if ((scopedpdu = ober_add_sequence(NULL)) == NULL) {
! 406: ober_free_elements(pdu);
1.4 martijn 407: goto fail;
408: }
1.8.2.1 ! tb 409: if (ober_printf_elements(scopedpdu, "xxe",
1.4 martijn 410: agent->v3->engineid, agent->v3->engineidlen,
411: agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
1.8.2.1 ! tb 412: ober_free_elements(pdu);
! 413: ober_free_elements(scopedpdu);
1.4 martijn 414: goto fail;
415: }
416: pdu = NULL;
417: if ((securityparams = agent->v3->sec->genparams(agent,
1.5 martijn 418: &securitysize, &cookie)) == NULL) {
1.8.2.1 ! tb 419: ober_free_elements(scopedpdu);
1.4 martijn 420: goto fail;
421: }
1.6 martijn 422: if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
423: if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
424: cookie)) == NULL)
425: goto fail;
1.8.2.1 ! tb 426: ober_free_elements(scopedpdu);
1.6 martijn 427: scopedpdu = encpdu;
428: }
1.8.2.1 ! tb 429: if (ober_printf_elements(message, "d{idxd}xe",
1.4 martijn 430: agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
431: (size_t) 1, agent->v3->sec->model, securityparams,
1.8 martijn 432: securitysize, scopedpdu) == NULL) {
1.8.2.1 ! tb 433: ober_free_elements(scopedpdu);
1.4 martijn 434: goto fail;
1.8 martijn 435: }
1.8.2.1 ! tb 436: if (ober_scanf_elements(message, "{SSe", &secparams) == -1)
1.5 martijn 437: goto fail;
1.8.2.1 ! tb 438: ober_set_writecallback(secparams, snmp_v3_secparamsoffset,
1.5 martijn 439: &secparamsoffset);
1.3 martijn 440: break;
441: }
442:
1.8.2.1 ! tb 443: if (ober_write_elements(&ber, message) == -1)
1.3 martijn 444: goto fail;
445: ret = ber_copy_writebuf(&ber, (void **)&packet);
446:
447: *len = (size_t) ret;
1.8.2.1 ! tb 448: ober_free(&ber);
1.3 martijn 449:
1.5 martijn 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:
1.3 martijn 458: fail:
1.5 martijn 459: if (agent->version == SNMP_V3)
460: agent->v3->sec->freecookie(cookie);
1.8.2.1 ! tb 461: ober_free_elements(message);
1.4 martijn 462: free(securityparams);
1.3 martijn 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;
1.4 martijn 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;
1.6 martijn 479: char *encpdu, *engineid;
480: size_t encpdulen, engineidlen;
481: void *cookie = NULL;
1.3 martijn 482:
483: bzero(&ber, sizeof(ber));
1.8.2.1 ! tb 484: ober_set_application(&ber, smi_application);
1.3 martijn 485:
1.8.2.1 ! tb 486: ober_set_readbuf(&ber, buf, buflen);
! 487: if ((message = ober_read_elements(&ber, NULL)) == NULL)
1.3 martijn 488: return NULL;
1.8.2.1 ! tb 489: ober_free(&ber);
1.3 martijn 490:
1.8.2.1 ! tb 491: if (ober_scanf_elements(message, "{de", &version, &payload) != 0)
1.3 martijn 492: goto fail;
493:
494: if (version != agent->version)
495: goto fail;
496:
1.4 martijn 497: switch (version) {
1.3 martijn 498: case SNMP_V1:
499: case SNMP_V2C:
1.8.2.1 ! tb 500: if (ober_scanf_elements(payload, "se", &community, &pdu) == -1)
1.3 martijn 501: goto fail;
502: if (strcmp(community, agent->community) != 0)
503: goto fail;
1.8.2.1 ! tb 504: ober_unlink_elements(payload);
! 505: ober_free_elements(message);
1.1 martijn 506: return pdu;
1.3 martijn 507: case SNMP_V3:
1.8.2.1 ! tb 508: if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
1.4 martijn 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,
1.6 martijn 515: secparamsoffset, secparams, secparamslen, msgflags[0],
516: &cookie) == -1) {
517: cookie = NULL;
1.4 martijn 518: goto fail;
1.6 martijn 519: }
520: if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
1.8.2.1 ! tb 521: if (ober_scanf_elements(scopedpdu, "x", &encpdu,
1.6 martijn 522: &encpdulen) == -1)
523: goto fail;
524: if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
525: encpdulen, cookie)) == NULL)
526: goto fail;
527: }
1.8.2.1 ! tb 528: if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid,
1.4 martijn 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: }
1.8.2.1 ! tb 536: pdu = ober_unlink_elements(ctxname);
1.4 martijn 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:
1.8.2.1 ! tb 544: ober_free_elements(message);
1.6 martijn 545: agent->v3->sec->freecookie(cookie);
1.4 martijn 546: return pdu;
1.1 martijn 547: }
1.3 martijn 548: /* NOTREACHED */
1.1 martijn 549:
550: fail:
1.6 martijn 551: if (version == SNMP_V3)
552: agent->v3->sec->freecookie(cookie);
1.8.2.1 ! tb 553: ober_free_elements(message);
1.1 martijn 554: return NULL;
1.5 martijn 555: }
556:
557: static void
558: snmp_v3_secparamsoffset(void *cookie, size_t offset)
559: {
560: size_t *spoffset = cookie;
561:
562: *spoffset = offset;
1.3 martijn 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;
1.8.2.1 ! tb 572: if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1)
1.3 martijn 573: return -1;
574: if ((*buf = malloc(ret)) == NULL)
575: return -1;
576: memcpy(*buf, bbuf, ret);
577: return ret;
1.1 martijn 578: }