1 /* $OpenBSD: snmp.c,v 1.11 2020/08/02 20:14:10 deraadt 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 *
snmp_v3_init(int level,const char * ctxname,size_t ctxnamelen,struct snmp_sec * sec)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
snmp_v3_setengineid(struct snmp_v3 * v3,char * engineid,size_t engineidlen)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 *
snmp_connect_v12(int fd,enum snmp_version version,const char * community)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 *
snmp_connect_v3(int fd,struct snmp_v3 * v3)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
snmp_free_agent(struct snmp_agent * agent)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
snmp_v3_free(struct snmp_v3 * v3)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 *
snmp_get(struct snmp_agent * agent,struct ber_oid * oid,size_t len)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
166 return snmp_resolve(agent, pdu, 1);
167 fail:
168 ober_free_elements(pdu);
169 return NULL;
170 }
171
172 struct ber_element *
snmp_getnext(struct snmp_agent * agent,struct ber_oid * oid,size_t len)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
178 if ((pdu = ober_add_sequence(NULL)) == NULL)
179 return NULL;
180 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
181 SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
182 goto fail;
183 for (i = 0; i < len; i++) {
184 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
185 if (varbind == NULL)
186 goto fail;
187 }
188
189 return snmp_resolve(agent, pdu, 1);
190 fail:
191 ober_free_elements(pdu);
192 return NULL;
193 }
194
195 int
snmp_trap(struct snmp_agent * agent,struct timespec * uptime,struct ber_oid * oid,struct ber_element * custvarbind)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
203 if ((pdu = ober_add_sequence(NULL)) == NULL)
204 return -1;
205 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
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;
213 if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
214 BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
215 goto fail;
216 if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
217 goto fail;
218 if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
219 goto fail;
220 if (custvarbind != NULL)
221 ober_link_elements(varbind, custvarbind);
222
223 snmp_resolve(agent, pdu, 0);
224 return 0;
225 fail:
226 ober_free_elements(pdu);
227 return -1;
228 }
229
230 struct ber_element *
snmp_getbulk(struct snmp_agent * agent,struct ber_oid * oid,size_t len,int non_repeaters,int max_repetitions)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
237 if ((pdu = ober_add_sequence(NULL)) == NULL)
238 return NULL;
239 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
240 SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
241 max_repetitions)) == NULL)
242 goto fail;
243 for (i = 0; i < len; i++) {
244 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]);
245 if (varbind == NULL)
246 goto fail;
247 }
248
249 return snmp_resolve(agent, pdu, 1);
250 fail:
251 ober_free_elements(pdu);
252 return NULL;
253 }
254
255 struct ber_element *
snmp_set(struct snmp_agent * agent,struct ber_element * vblist)256 snmp_set(struct snmp_agent *agent, struct ber_element *vblist)
257 {
258 struct ber_element *pdu;
259
260 if ((pdu = ober_add_sequence(NULL)) == NULL)
261 return NULL;
262 if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT,
263 SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) {
264 ober_free_elements(pdu);
265 ober_free_elements(vblist);
266 return NULL;
267 }
268
269 return snmp_resolve(agent, pdu, 1);
270 }
271
272 static struct ber_element *
snmp_resolve(struct snmp_agent * agent,struct ber_element * pdu,int reply)273 snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
274 {
275 struct ber_element *varbind;
276 struct ber_oid oid;
277 struct timespec start, now;
278 struct pollfd pfd;
279 char *message;
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
287 if (ober_scanf_elements(pdu, "{i", &reqid) != 0) {
288 errno = EINVAL;
289 ober_free_elements(pdu);
290 return NULL;
291 }
292
293 if ((message = snmp_package(agent, pdu, &len)) == NULL)
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) {
322 ret = send(agent->fd, message, len, MSG_DONTWAIT);
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;
339 if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
340 tries--;
341 direction = POLLOUT;
342 errno = EPROTO;
343 continue;
344 }
345 /* Validate pdu format and check request id */
346 if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
347 varbind->be_encoding != BER_TYPE_SEQUENCE) {
348 errno = EPROTO;
349 direction = POLLOUT;
350 tries--;
351 continue;
352 }
353 if (rreqid != reqid && rreqid != 0) {
354 errno = EPROTO;
355 direction = POLLOUT;
356 tries--;
357 continue;
358 }
359 for (varbind = varbind->be_sub; varbind != NULL;
360 varbind = varbind->be_next) {
361 if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) {
362 errno = EPROTO;
363 direction = POLLOUT;
364 tries--;
365 break;
366 }
367 }
368 if (varbind != NULL)
369 continue;
370
371 free(message);
372 return pdu;
373 }
374
375 fail:
376 free(message);
377 return NULL;
378 }
379
380 static char *
snmp_package(struct snmp_agent * agent,struct ber_element * pdu,size_t * len)381 snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
382 {
383 struct ber ber;
384 struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu;
385 ssize_t securitysize, ret;
386 size_t secparamsoffset;
387 char *securityparams = NULL, *packet = NULL;
388 long long msgid;
389 void *cookie = NULL;
390
391 bzero(&ber, sizeof(ber));
392 ober_set_application(&ber, smi_application);
393
394 if ((message = ober_add_sequence(NULL)) == NULL) {
395 ober_free_elements(pdu);
396 goto fail;
397 }
398
399 switch (agent->version) {
400 case SNMP_V1:
401 case SNMP_V2C:
402 if (ober_printf_elements(message, "dse", agent->version,
403 agent->community, pdu) == NULL) {
404 ober_free_elements(pdu);
405 goto fail;
406 }
407 break;
408 case SNMP_V3:
409 msgid = arc4random_uniform(2147483647);
410 if ((scopedpdu = ober_add_sequence(NULL)) == NULL) {
411 ober_free_elements(pdu);
412 goto fail;
413 }
414 if (ober_printf_elements(scopedpdu, "xxe",
415 agent->v3->engineid, agent->v3->engineidlen,
416 agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
417 ober_free_elements(pdu);
418 ober_free_elements(scopedpdu);
419 goto fail;
420 }
421 pdu = NULL;
422 if ((securityparams = agent->v3->sec->genparams(agent,
423 &securitysize, &cookie)) == NULL) {
424 ober_free_elements(scopedpdu);
425 goto fail;
426 }
427 if (agent->v3->level & SNMP_MSGFLAG_PRIV) {
428 if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu,
429 cookie)) == NULL)
430 goto fail;
431 ober_free_elements(scopedpdu);
432 scopedpdu = encpdu;
433 }
434 if (ober_printf_elements(message, "d{idxd}xe",
435 agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
436 (size_t) 1, agent->v3->sec->model, securityparams,
437 securitysize, scopedpdu) == NULL) {
438 ober_free_elements(scopedpdu);
439 goto fail;
440 }
441 if (ober_scanf_elements(message, "{SSe", &secparams) == -1)
442 goto fail;
443 ober_set_writecallback(secparams, snmp_v3_secparamsoffset,
444 &secparamsoffset);
445 break;
446 }
447
448 if (ober_write_elements(&ber, message) == -1)
449 goto fail;
450 ret = ber_copy_writebuf(&ber, (void **)&packet);
451
452 *len = (size_t) ret;
453 ober_free(&ber);
454
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
463 fail:
464 if (agent->version == SNMP_V3)
465 agent->v3->sec->freecookie(cookie);
466 ober_free_elements(message);
467 free(securityparams);
468 return packet;
469 }
470
471 static struct ber_element *
snmp_unpackage(struct snmp_agent * agent,char * buf,size_t buflen)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;
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;
484 char *encpdu, *engineid;
485 size_t encpdulen, engineidlen;
486 void *cookie = NULL;
487
488 bzero(&ber, sizeof(ber));
489 ober_set_application(&ber, smi_application);
490
491 ober_set_readbuf(&ber, buf, buflen);
492 if ((message = ober_read_elements(&ber, NULL)) == NULL)
493 return NULL;
494 ober_free(&ber);
495
496 if (ober_scanf_elements(message, "{de", &version, &payload) != 0)
497 goto fail;
498
499 if (version != agent->version)
500 goto fail;
501
502 switch (version) {
503 case SNMP_V1:
504 case SNMP_V2C:
505 if (ober_scanf_elements(payload, "se", &community, &pdu) == -1)
506 goto fail;
507 if (strcmp(community, agent->community) != 0)
508 goto fail;
509 ober_unlink_elements(payload);
510 ober_free_elements(message);
511 return pdu;
512 case SNMP_V3:
513 if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
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,
520 secparamsoffset, secparams, secparamslen, msgflags[0],
521 &cookie) == -1) {
522 cookie = NULL;
523 goto fail;
524 }
525 if (msgflags[0] & SNMP_MSGFLAG_PRIV) {
526 if (ober_scanf_elements(scopedpdu, "x", &encpdu,
527 &encpdulen) == -1)
528 goto fail;
529 if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu,
530 encpdulen, cookie)) == NULL)
531 goto fail;
532 }
533 if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid,
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 }
541 pdu = ober_unlink_elements(ctxname);
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
549 ober_free_elements(message);
550 agent->v3->sec->freecookie(cookie);
551 return pdu;
552 }
553 /* NOTREACHED */
554
555 fail:
556 if (version == SNMP_V3)
557 agent->v3->sec->freecookie(cookie);
558 ober_free_elements(message);
559 return NULL;
560 }
561
562 static void
snmp_v3_secparamsoffset(void * cookie,size_t offset)563 snmp_v3_secparamsoffset(void *cookie, size_t offset)
564 {
565 size_t *spoffset = cookie;
566
567 *spoffset = offset;
568 }
569
570 ssize_t
ber_copy_writebuf(struct ber * ber,void ** buf)571 ber_copy_writebuf(struct ber *ber, void **buf)
572 {
573 char *bbuf;
574 ssize_t ret;
575
576 *buf = NULL;
577 if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1)
578 return -1;
579 if ((*buf = malloc(ret)) == NULL)
580 return -1;
581 memcpy(*buf, bbuf, ret);
582 return ret;
583 }
584