Annotation of src/usr.bin/snmp/snmp.c, Revision 1.10
1.10 ! martijn 1: /* $OpenBSD: snmp.c,v 1.9 2019/10/24 12:39:26 tb 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;
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;
164:
165: return snmp_resolve(agent, pdu, 1);
166: fail:
1.9 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.9 tb 177: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 178: return NULL;
1.9 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.9 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.9 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.9 tb 201: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 202: return -1;
1.9 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.9 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.9 tb 216: if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
1.1 martijn 217: goto fail;
218: if (custvarbind != NULL)
1.9 tb 219: ober_link_elements(varbind, custvarbind);
1.1 martijn 220:
221: snmp_resolve(agent, pdu, 0);
222: return 0;
223: fail:
1.9 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.9 tb 235: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.1 martijn 236: return NULL;
1.9 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.9 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.9 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.9 tb 257: if ((pdu = ober_add_sequence(NULL)) == NULL)
1.7 martijn 258: return NULL;
1.9 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.9 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.9 tb 284: if (ober_scanf_elements(pdu, "{i", &reqid) != 0) {
1.1 martijn 285: errno = EINVAL;
1.9 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.9 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.9 tb 358: if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) {
1.2 martijn 359: errno = EPROTO;
360: direction = POLLOUT;
361: tries--;
1.10 ! martijn 362: break;
1.2 martijn 363: }
1.1 martijn 364: }
1.10 ! martijn 365: if (varbind != NULL)
! 366: continue;
1.1 martijn 367:
1.3 martijn 368: free(message);
369: return pdu;
370: }
371:
372: fail:
373: free(message);
374: return NULL;
375: }
376:
377: static char *
378: snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
379: {
380: struct ber ber;
1.6 martijn 381: struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
1.4 martijn 382: ssize_t securitysize, ret;
1.5 martijn 383: size_t secparamsoffset;
1.4 martijn 384: char *securityparams = NULL, *packet = NULL;
385: long long msgid;
1.5 martijn 386: void *cookie = NULL;
1.3 martijn 387:
388: bzero(&ber, sizeof(ber));
1.9 tb 389: ober_set_application(&ber, smi_application);
1.3 martijn 390:
1.9 tb 391: if ((message = ober_add_sequence(NULL)) == NULL) {
392: ober_free_elements(pdu);
1.3 martijn 393: goto fail;
394: }
395:
396: switch (agent->version) {
397: case SNMP_V1:
398: case SNMP_V2C:
1.9 tb 399: if (ober_printf_elements(message, "dse", agent->version,
1.3 martijn 400: agent->community, pdu) == NULL) {
1.9 tb 401: ober_free_elements(pdu);
1.3 martijn 402: goto fail;
403: }
404: break;
405: case SNMP_V3:
1.4 martijn 406: msgid = arc4random_uniform(2147483647);
1.9 tb 407: if ((scopedpdu = ober_add_sequence(NULL)) == NULL) {
408: ober_free_elements(pdu);
1.4 martijn 409: goto fail;
410: }
1.9 tb 411: if (ober_printf_elements(scopedpdu, "xxe",
1.4 martijn 412: agent->v3->engineid, agent->v3->engineidlen,
413: agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
1.9 tb 414: ober_free_elements(pdu);
415: ober_free_elements(scopedpdu);
1.4 martijn 416: goto fail;
417: }
418: pdu = NULL;
419: if ((securityparams = agent->v3->sec->genparams(agent,
1.5 martijn 420: &securitysize, &cookie)) == NULL) {
1.9 tb 421: ober_free_elements(scopedpdu);
1.4 martijn 422: goto fail;
423: }
1.6 martijn 424: if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
425: if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
426: cookie)) == NULL)
427: goto fail;
1.9 tb 428: ober_free_elements(scopedpdu);
1.6 martijn 429: scopedpdu = encpdu;
430: }
1.9 tb 431: if (ober_printf_elements(message, "d{idxd}xe",
1.4 martijn 432: agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
433: (size_t) 1, agent->v3->sec->model, securityparams,
1.8 martijn 434: securitysize, scopedpdu) == NULL) {
1.9 tb 435: ober_free_elements(scopedpdu);
1.4 martijn 436: goto fail;
1.8 martijn 437: }
1.9 tb 438: if (ober_scanf_elements(message, "{SSe", &secparams) == -1)
1.5 martijn 439: goto fail;
1.9 tb 440: ober_set_writecallback(secparams, snmp_v3_secparamsoffset,
1.5 martijn 441: &secparamsoffset);
1.3 martijn 442: break;
443: }
444:
1.9 tb 445: if (ober_write_elements(&ber, message) == -1)
1.3 martijn 446: goto fail;
447: ret = ber_copy_writebuf(&ber, (void **)&packet);
448:
449: *len = (size_t) ret;
1.9 tb 450: ober_free(&ber);
1.3 martijn 451:
1.5 martijn 452: if (agent->version == SNMP_V3 && packet != NULL) {
453: if (agent->v3->sec->finalparams(agent, packet,
454: ret, secparamsoffset, cookie) == -1) {
455: free(packet);
456: packet = NULL;
457: }
458: }
459:
1.3 martijn 460: fail:
1.5 martijn 461: if (agent->version == SNMP_V3)
462: agent->v3->sec->freecookie(cookie);
1.9 tb 463: ober_free_elements(message);
1.4 martijn 464: free(securityparams);
1.3 martijn 465: return packet;
466: }
467:
468: static struct ber_element *
469: snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
470: {
471: struct ber ber;
472: enum snmp_version version;
473: char *community;
474: struct ber_element *pdu;
1.4 martijn 475: long long msgid, model;
476: int msgsz;
477: char *msgflags, *secparams;
478: size_t msgflagslen, secparamslen;
479: struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
480: off_t secparamsoffset;
1.6 martijn 481: char *encpdu, *engineid;
482: size_t encpdulen, engineidlen;
483: void *cookie = NULL;
1.3 martijn 484:
485: bzero(&ber, sizeof(ber));
1.9 tb 486: ober_set_application(&ber, smi_application);
1.3 martijn 487:
1.9 tb 488: ober_set_readbuf(&ber, buf, buflen);
489: if ((message = ober_read_elements(&ber, NULL)) == NULL)
1.3 martijn 490: return NULL;
1.9 tb 491: ober_free(&ber);
1.3 martijn 492:
1.9 tb 493: if (ober_scanf_elements(message, "{de", &version, &payload) != 0)
1.3 martijn 494: goto fail;
495:
496: if (version != agent->version)
497: goto fail;
498:
1.4 martijn 499: switch (version) {
1.3 martijn 500: case SNMP_V1:
501: case SNMP_V2C:
1.9 tb 502: if (ober_scanf_elements(payload, "se", &community, &pdu) == -1)
1.3 martijn 503: goto fail;
504: if (strcmp(community, agent->community) != 0)
505: goto fail;
1.9 tb 506: ober_unlink_elements(payload);
507: ober_free_elements(message);
1.1 martijn 508: return pdu;
1.3 martijn 509: case SNMP_V3:
1.9 tb 510: if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
1.4 martijn 511: &msgflags, &msgflagslen, &model, &secparamsoffset,
512: &secparams, &secparamslen, &scopedpdu) == -1)
513: goto fail;
514: if (msgflagslen != 1)
515: goto fail;
516: if (agent->v3->sec->parseparams(agent, buf, buflen,
1.6 martijn 517: secparamsoffset, secparams, secparamslen, msgflags[0],
518: &cookie) == -1) {
519: cookie = NULL;
1.4 martijn 520: goto fail;
1.6 martijn 521: }
522: if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
1.9 tb 523: if (ober_scanf_elements(scopedpdu, "x", &encpdu,
1.6 martijn 524: &encpdulen) == -1)
525: goto fail;
526: if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
527: encpdulen, cookie)) == NULL)
528: goto fail;
529: }
1.9 tb 530: if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid,
1.4 martijn 531: &engineidlen, &ctxname) == -1)
532: goto fail;
533: if (!agent->v3->engineidset) {
534: if (snmp_v3_setengineid(agent->v3, engineid,
535: engineidlen) == -1)
536: goto fail;
537: }
1.9 tb 538: pdu = ober_unlink_elements(ctxname);
1.4 martijn 539: /* Accept reports, so we can continue if possible */
540: if (pdu->be_type != SNMP_C_REPORT) {
541: if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
542: (agent->v3->level & SNMP_MSGFLAG_SECMASK))
543: goto fail;
544: }
545:
1.9 tb 546: ober_free_elements(message);
1.6 martijn 547: agent->v3->sec->freecookie(cookie);
1.4 martijn 548: return pdu;
1.1 martijn 549: }
1.3 martijn 550: /* NOTREACHED */
1.1 martijn 551:
552: fail:
1.6 martijn 553: if (version == SNMP_V3)
554: agent->v3->sec->freecookie(cookie);
1.9 tb 555: ober_free_elements(message);
1.1 martijn 556: return NULL;
1.5 martijn 557: }
558:
559: static void
560: snmp_v3_secparamsoffset(void *cookie, size_t offset)
561: {
562: size_t *spoffset = cookie;
563:
564: *spoffset = offset;
1.3 martijn 565: }
566:
567: ssize_t
568: ber_copy_writebuf(struct ber *ber, void **buf)
569: {
570: char *bbuf;
571: ssize_t ret;
572:
573: *buf = NULL;
1.9 tb 574: if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1)
1.3 martijn 575: return -1;
576: if ((*buf = malloc(ret)) == NULL)
577: return -1;
578: memcpy(*buf, bbuf, ret);
579: return ret;
1.1 martijn 580: }