Annotation of src/usr.bin/snmp/snmp.c, Revision 1.4
1.4 ! martijn 1: /* $OpenBSD: snmp.c,v 1.3 2019/09/18 09:44:38 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 *);
! 42:
! 43: struct snmp_v3 *
! 44: snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
! 45: struct snmp_sec *sec)
! 46: {
! 47: struct snmp_v3 *v3;
! 48:
! 49: if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level ||
! 50: sec == NULL) {
! 51: errno = EINVAL;
! 52: return NULL;
! 53: }
! 54: if ((v3 = calloc(1, sizeof(*v3))) == NULL)
! 55: return NULL;
! 56:
! 57: v3->level = level | SNMP_MSGFLAG_REPORT;
! 58: v3->ctxnamelen = ctxnamelen;
! 59: if (ctxnamelen != 0) {
! 60: if ((v3->ctxname = malloc(ctxnamelen)) == NULL) {
! 61: free(v3);
! 62: return NULL;
! 63: }
! 64: memcpy(v3->ctxname, ctxname, ctxnamelen);
! 65: }
! 66: v3->sec = sec;
! 67: return v3;
! 68: }
! 69:
! 70: int
! 71: snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen)
! 72: {
! 73: if (v3->engineidset)
! 74: free(v3->engineid);
! 75: if ((v3->engineid = malloc(engineidlen)) == NULL)
! 76: return -1;
! 77: memcpy(v3->engineid, engineid, engineidlen);
! 78: v3->engineidlen = engineidlen;
! 79: v3->engineidset = 1;
! 80: return 0;
! 81: }
1.1 martijn 82:
83: struct snmp_agent *
84: snmp_connect_v12(int fd, enum snmp_version version, const char *community)
85: {
86: struct snmp_agent *agent;
87:
88: if (version != SNMP_V1 && version != SNMP_V2C) {
89: errno = EINVAL;
90: return NULL;
91: }
92: if ((agent = malloc(sizeof(*agent))) == NULL)
93: return NULL;
94: agent->fd = fd;
95: agent->version = version;
96: if ((agent->community = strdup(community)) == NULL)
97: goto fail;
98: agent->timeout = 1;
99: agent->retries = 5;
1.4 ! martijn 100: agent->v3 = NULL;
1.1 martijn 101: return agent;
102:
103: fail:
104: free(agent);
105: return NULL;
106: }
107:
1.4 ! martijn 108: struct snmp_agent *
! 109: snmp_connect_v3(int fd, struct snmp_v3 *v3)
! 110: {
! 111: struct snmp_agent *agent;
! 112:
! 113: if ((agent = malloc(sizeof(*agent))) == NULL)
! 114: return NULL;
! 115: agent->fd = fd;
! 116: agent->version = SNMP_V3;
! 117: agent->v3 = v3;
! 118: agent->timeout = 1;
! 119: agent->retries = 5;
! 120: agent->community = NULL;
! 121:
! 122: if (v3->sec->init(agent) == -1) {
! 123: snmp_free_agent(agent);
! 124: return NULL;
! 125: }
! 126: return agent;
! 127: }
! 128:
1.1 martijn 129: void
130: snmp_free_agent(struct snmp_agent *agent)
131: {
132: free(agent->community);
1.4 ! martijn 133: if (agent->v3 != NULL)
! 134: snmp_v3_free(agent->v3);
1.1 martijn 135: free(agent);
136: }
137:
1.4 ! martijn 138: static void
! 139: snmp_v3_free(struct snmp_v3 *v3)
! 140: {
! 141: v3->sec->free(v3->sec->data);
! 142: free(v3->sec);
! 143: free(v3->ctxname);
! 144: free(v3->engineid);
! 145: free(v3);
! 146: }
! 147:
1.1 martijn 148: struct ber_element *
149: snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
150: {
151: struct ber_element *pdu, *varbind;
152: size_t i;
153:
154: if ((pdu = ber_add_sequence(NULL)) == NULL)
155: return NULL;
156: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
157: SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
158: goto fail;
159: for (i = 0; i < len; i++)
160: varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
161: if (varbind == NULL)
162: goto fail;
163:
164: return snmp_resolve(agent, pdu, 1);
165: fail:
166: ber_free_elements(pdu);
167: return NULL;
168: }
169:
170: struct ber_element *
171: snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
172: {
173: struct ber_element *pdu, *varbind;
174: size_t i;
175:
176: if ((pdu = ber_add_sequence(NULL)) == NULL)
177: return NULL;
178: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
179: SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
180: goto fail;
181: for (i = 0; i < len; i++)
182: varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
183: if (varbind == NULL)
184: goto fail;
185:
186: return snmp_resolve(agent, pdu, 1);
187: fail:
188: ber_free_elements(pdu);
189: return NULL;
190: }
191:
192: int
193: snmp_trap(struct snmp_agent *agent, struct timespec *uptime,
194: struct ber_oid *oid, struct ber_element *custvarbind)
195: {
196: struct ber_element *pdu, *varbind;
197: struct ber_oid sysuptime, trap;
198: long long ticks;
199:
200: if ((pdu = ber_add_sequence(NULL)) == NULL)
201: return -1;
202: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
203: SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL)
204: goto fail;
205:
206: ticks = uptime->tv_sec * 100;
207: ticks += uptime->tv_nsec / 10000000;
208: if (smi_string2oid("sysUpTime.0", &sysuptime) == -1)
209: goto fail;
210: if ((varbind = ber_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
211: BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
212: goto fail;
213: if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
214: goto fail;
215: if ((varbind = ber_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
216: goto fail;
217: if (custvarbind != NULL)
218: ber_link_elements(varbind, custvarbind);
219:
220: snmp_resolve(agent, pdu, 0);
221: return 0;
222: fail:
223: ber_free_elements(pdu);
224: return -1;
225: }
226:
227: struct ber_element *
228: snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len,
229: int non_repeaters, int max_repetitions)
230: {
231: struct ber_element *pdu, *varbind;
232: size_t i;
233:
234: if ((pdu = ber_add_sequence(NULL)) == NULL)
235: return NULL;
236: if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
237: SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
238: max_repetitions)) == NULL)
239: goto fail;
240: for (i = 0; i < len; i++)
241: varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
242: if (varbind == NULL)
243: goto fail;
244:
245: return snmp_resolve(agent, pdu, 1);
246: fail:
247: ber_free_elements(pdu);
248: return NULL;
249: }
250:
251: static struct ber_element *
252: snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
253: {
1.3 martijn 254: struct ber_element *varbind;
1.1 martijn 255: struct ber_oid oid;
256: struct timespec start, now;
257: struct pollfd pfd;
1.3 martijn 258: char *message;
1.1 martijn 259: ssize_t len;
260: long long reqid, rreqid;
261: short direction;
262: int to, nfds, ret;
263: int tries;
264: char buf[READ_BUF_SIZE];
265:
266: if (ber_scanf_elements(pdu, "{i", &reqid) != 0) {
267: errno = EINVAL;
268: ber_free_elements(pdu);
269: return NULL;
270: }
271:
1.3 martijn 272: if ((message = snmp_package(agent, pdu, &len)) == NULL)
1.1 martijn 273: return NULL;
274:
275: clock_gettime(CLOCK_MONOTONIC, &start);
276: memcpy(&now, &start, sizeof(now));
277: direction = POLLOUT;
278: tries = agent->retries + 1;
279: while (tries) {
280: pfd.fd = agent->fd;
281: pfd.events = direction;
282: if (agent->timeout > 0) {
283: to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000;
284: to -= (now.tv_nsec - start.tv_nsec) / 1000000;
285: } else
286: to = INFTIM;
287: nfds = poll(&pfd, 1, to);
288: if (nfds == 0) {
289: errno = ETIMEDOUT;
290: direction = POLLOUT;
291: tries--;
292: continue;
293: }
294: if (nfds == -1) {
295: if (errno == EINTR)
296: continue;
297: else
298: goto fail;
299: }
300: if (direction == POLLOUT) {
1.3 martijn 301: ret = send(agent->fd, message, len, MSG_DONTWAIT);
1.1 martijn 302: if (ret == -1)
303: goto fail;
304: if (ret < len) {
305: errno = EBADMSG;
306: goto fail;
307: }
308: if (!reply)
309: return NULL;
310: direction = POLLIN;
311: continue;
312: }
313: ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT);
314: if (ret == 0)
315: errno = ECONNRESET;
316: if (ret <= 0)
317: goto fail;
1.3 martijn 318: if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
1.2 martijn 319: tries--;
320: direction = POLLOUT;
321: errno = EPROTO;
1.1 martijn 322: continue;
1.2 martijn 323: }
1.1 martijn 324: /* Validate pdu format and check request id */
325: if (ber_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
1.2 martijn 326: varbind->be_encoding != BER_TYPE_SEQUENCE) {
327: errno = EPROTO;
328: direction = POLLOUT;
329: tries--;
330: continue;
331: }
1.4 ! martijn 332: if (rreqid != reqid && rreqid != 0) {
1.2 martijn 333: errno = EPROTO;
334: direction = POLLOUT;
335: tries--;
1.1 martijn 336: continue;
1.2 martijn 337: }
1.1 martijn 338: for (varbind = varbind->be_sub; varbind != NULL;
339: varbind = varbind->be_next) {
1.2 martijn 340: if (ber_scanf_elements(varbind, "{oS}", &oid) != 0) {
341: errno = EPROTO;
342: direction = POLLOUT;
343: tries--;
1.4 ! martijn 344: continue;
1.2 martijn 345: }
1.1 martijn 346: }
347:
1.3 martijn 348: free(message);
349: return pdu;
350: }
351:
352: fail:
353: free(message);
354: return NULL;
355: }
356:
357: static char *
358: snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
359: {
360: struct ber ber;
1.4 ! martijn 361: struct ber_element *message, *scopedpdu = NULL;
! 362: ssize_t securitysize, ret;
! 363: char *securityparams = NULL, *packet = NULL;
! 364: long long msgid;
1.3 martijn 365:
366: bzero(&ber, sizeof(ber));
367: ber_set_application(&ber, smi_application);
368:
369: if ((message = ber_add_sequence(NULL)) == NULL) {
370: ber_free_elements(pdu);
371: goto fail;
372: }
373:
374: switch (agent->version) {
375: case SNMP_V1:
376: case SNMP_V2C:
377: if (ber_printf_elements(message, "dse", agent->version,
378: agent->community, pdu) == NULL) {
379: ber_free_elements(pdu);
380: goto fail;
381: }
382: break;
383: case SNMP_V3:
1.4 ! martijn 384: msgid = arc4random_uniform(2147483647);
! 385: if ((scopedpdu = ber_add_sequence(NULL)) == NULL) {
! 386: ber_free_elements(pdu);
! 387: goto fail;
! 388: }
! 389: if (ber_printf_elements(scopedpdu, "xxe",
! 390: agent->v3->engineid, agent->v3->engineidlen,
! 391: agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
! 392: ber_free_elements(pdu);
! 393: ber_free_elements(scopedpdu);
! 394: goto fail;
! 395: }
! 396: pdu = NULL;
! 397: if ((securityparams = agent->v3->sec->genparams(agent,
! 398: &securitysize)) == NULL) {
! 399: ber_free_elements(scopedpdu);
! 400: goto fail;
! 401: }
! 402: if (ber_printf_elements(message, "d{idxd}xe",
! 403: agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
! 404: (size_t) 1, agent->v3->sec->model, securityparams,
! 405: securitysize, scopedpdu) == NULL)
! 406: goto fail;
1.3 martijn 407: break;
408: }
409:
410: if (ber_write_elements(&ber, message) == -1)
411: goto fail;
412: ret = ber_copy_writebuf(&ber, (void **)&packet);
413:
414: *len = (size_t) ret;
415: ber_free(&ber);
416:
417: fail:
418: ber_free_elements(message);
1.4 ! martijn 419: free(securityparams);
1.3 martijn 420: return packet;
421: }
422:
423: static struct ber_element *
424: snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
425: {
426: struct ber ber;
427: enum snmp_version version;
428: char *community;
429: struct ber_element *pdu;
1.4 ! martijn 430: long long msgid, model;
! 431: int msgsz;
! 432: char *msgflags, *secparams;
! 433: size_t msgflagslen, secparamslen;
! 434: struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
! 435: off_t secparamsoffset;
! 436: char *engineid;
! 437: size_t engineidlen;
1.3 martijn 438:
439: bzero(&ber, sizeof(ber));
440: ber_set_application(&ber, smi_application);
441:
442: ber_set_readbuf(&ber, buf, buflen);
443: if ((message = ber_read_elements(&ber, NULL)) == NULL)
444: return NULL;
445: ber_free(&ber);
446:
447: if (ber_scanf_elements(message, "{de", &version, &payload) != 0)
448: goto fail;
449:
450: if (version != agent->version)
451: goto fail;
452:
1.4 ! martijn 453: switch (version) {
1.3 martijn 454: case SNMP_V1:
455: case SNMP_V2C:
456: if (ber_scanf_elements(payload, "se", &community, &pdu) == -1)
457: goto fail;
458: if (strcmp(community, agent->community) != 0)
459: goto fail;
460: ber_unlink_elements(payload);
1.1 martijn 461: ber_free_elements(message);
462: return pdu;
1.3 martijn 463: case SNMP_V3:
1.4 ! martijn 464: if (ber_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
! 465: &msgflags, &msgflagslen, &model, &secparamsoffset,
! 466: &secparams, &secparamslen, &scopedpdu) == -1)
! 467: goto fail;
! 468: if (msgflagslen != 1)
! 469: goto fail;
! 470: if (agent->v3->sec->parseparams(agent, buf, buflen,
! 471: secparamsoffset, secparams, secparamslen,
! 472: msgflags[0]) == -1)
! 473: goto fail;
! 474: if (ber_scanf_elements(scopedpdu, "{xeS{", &engineid,
! 475: &engineidlen, &ctxname) == -1)
! 476: goto fail;
! 477: if (!agent->v3->engineidset) {
! 478: if (snmp_v3_setengineid(agent->v3, engineid,
! 479: engineidlen) == -1)
! 480: goto fail;
! 481: }
! 482: pdu = ber_unlink_elements(ctxname);
! 483: /* Accept reports, so we can continue if possible */
! 484: if (pdu->be_type != SNMP_C_REPORT) {
! 485: if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
! 486: (agent->v3->level & SNMP_MSGFLAG_SECMASK))
! 487: goto fail;
! 488: }
! 489:
! 490: ber_free_elements(message);
! 491: return pdu;
1.1 martijn 492: }
1.3 martijn 493: /* NOTREACHED */
1.1 martijn 494:
495: fail:
496: ber_free_elements(message);
497: return NULL;
1.3 martijn 498: }
499:
500: ssize_t
501: ber_copy_writebuf(struct ber *ber, void **buf)
502: {
503: char *bbuf;
504: ssize_t ret;
505:
506: *buf = NULL;
507: if ((ret = ber_get_writebuf(ber, (void **)&bbuf)) == -1)
508: return -1;
509: if ((*buf = malloc(ret)) == NULL)
510: return -1;
511: memcpy(*buf, bbuf, ret);
512: return ret;
1.1 martijn 513: }