GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpctl/snmpclient.c Lines: 0 201 0.0 %
Date: 2017-11-13 Branches: 0 141 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: snmpclient.c,v 1.14 2017/08/10 16:03:10 rob Exp $	*/
2
3
/*
4
 * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/queue.h>
20
#include <sys/types.h>
21
#include <sys/stat.h>
22
#include <sys/socket.h>
23
#include <sys/tree.h>
24
25
#include <net/if.h>
26
#include <netinet/in.h>
27
#include <arpa/inet.h>
28
29
#include <stdlib.h>
30
#include <stdio.h>
31
#include <unistd.h>
32
#include <netdb.h>
33
#include <errno.h>
34
#include <event.h>
35
#include <fcntl.h>
36
#include <string.h>
37
#include <ctype.h>
38
#include <poll.h>
39
#include <err.h>
40
#include <pwd.h>
41
#include <vis.h>
42
43
#include "snmpd.h"
44
#include "mib.h"
45
#include "ber.h"
46
#include "parser.h"
47
48
struct snmpc {
49
	size_t			 sc_root_len;
50
	struct ber_oid		 sc_root_oid;
51
	struct ber_oid		 sc_last_oid;
52
	struct ber_oid		 sc_oid;
53
	struct sockaddr_storage	 sc_addr;
54
	socklen_t		 sc_addr_len;
55
	u_int32_t		 sc_msgid;
56
	int			 sc_fd;
57
	int			 sc_retry;
58
	int			 sc_retry_max;
59
	const char		*sc_community;
60
	int			 sc_version;
61
	int			 sc_nresp;
62
};
63
64
#define	SNMPC_RETRY_MAX		3
65
#define SNMPC_COMMUNITY		"public"
66
#define SNMPC_OID_DEFAULT	"system"
67
#define SNMPC_OID_ALL		"1.0"
68
#define SNMPC_MAXREPETITIONS	10
69
70
#define	SNMPC_DATEANDTIME_LEN		11
71
#define	SNMPC_DATEANDTIME_SHORT_LEN	8
72
73
void	 snmpc_run(struct snmpc *, enum actions, const char *);
74
void	 snmpc_request(struct snmpc *, u_long);
75
int	 snmpc_response(struct snmpc *);
76
int	 snmpc_sendreq(struct snmpc *, u_long);
77
int	 snmpc_recvresp(int, int, u_int32_t, struct ber_element **);
78
79
struct display_hint *
80
	 snmpc_display_hint_lookup(struct ber_oid *);
81
void	 snmpc_display_hint(struct ber_oid *, char **);
82
char	*snmpc_physaddress(char *);
83
char	*snmpc_dateandtime(char *);
84
85
struct display_hint {
86
	struct ber_oid		 oid;
87
	char			*(*print)(char *);
88
} display_hints[] = {
89
	{ { { MIB_ifPhysAddress } },		snmpc_physaddress },
90
	{ { { MIB_ipNetToMediaPhysAddress } },	snmpc_physaddress },
91
	{ { { MIB_hrSystemDate } },		snmpc_dateandtime }
92
};
93
94
void
95
snmpclient(struct parse_result *res)
96
{
97
	struct snmpc		 sc;
98
	struct addrinfo		 hints, *ai, *ai0;
99
	int			 s;
100
	int			 error;
101
	u_int			 i;
102
	struct passwd		*pw;
103
	struct parse_val	*oid;
104
105
	for (i = 0; i < sizeof(display_hints) / sizeof(display_hints[0]); i++)
106
		smi_oidlen(&display_hints[i].oid);
107
108
	bzero(&sc, sizeof(sc));
109
110
	/* Get client configuration */
111
	if (res->community == NULL)
112
		res->community = strdup(SNMPC_COMMUNITY);
113
	if (res->version == -1 || res->version > SNMP_V2)
114
		res->version = SNMP_V2;
115
	if (res->community == NULL)
116
		err(1, "strdup");
117
118
	/* Checks */
119
	if ((res->action == BULKWALK) && (res->version < SNMP_V2))
120
		errx(1, "invalid version for bulkwalk");
121
122
	/* Resolve target host name */
123
	bzero(&hints, sizeof(hints));
124
	hints.ai_family = PF_UNSPEC;
125
	hints.ai_socktype = SOCK_DGRAM;
126
	error = getaddrinfo(res->host, "snmp", &hints, &ai0);
127
	if (error)
128
		errx(1, "%s", gai_strerror(error));
129
	s = -1;
130
	for (ai = ai0; ai; ai = ai->ai_next) {
131
		if ((s = socket(ai->ai_family, ai->ai_socktype,
132
                    ai->ai_protocol)) == -1) {
133
			continue;
134
		}
135
		/* use first available address */
136
 		break;
137
	}
138
	if (s == -1)
139
		errx(1, "invalid host");
140
141
	bcopy(ai->ai_addr, &sc.sc_addr, ai->ai_addrlen);
142
	sc.sc_addr_len = ai->ai_addrlen;
143
	freeaddrinfo(ai0);
144
145
	/*
146
	 * Drop privileges to mitigate the risk when running as root.
147
	 */
148
	if (geteuid() == 0) {
149
		if ((pw = getpwnam(SNMPD_USER)) == NULL)
150
			err(1, "snmpctl: getpwnam");
151
#ifndef DEBUG
152
		if (chroot(pw->pw_dir) == -1)
153
			err(1, "snmpctl: chroot");
154
		if (chdir("/") == -1)
155
			err(1, "snmpctl: chdir(\"/\")");
156
		if (setgroups(1, &pw->pw_gid) ||
157
		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
158
		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
159
			err(1, "snmpctl: cannot drop privileges");
160
#endif
161
	}
162
163
	if (pledge("stdio dns flock rpath cpath wpath", NULL) == -1)
164
		fatal("pledge");
165
166
	sc.sc_fd = s;
167
	sc.sc_community = res->community;
168
	sc.sc_version = res->version;
169
	sc.sc_retry_max = SNMPC_RETRY_MAX;
170
171
	if (TAILQ_EMPTY(&res->oids)) {
172
			snmpc_run(&sc, res->action, SNMPC_OID_DEFAULT);
173
	} else {
174
		TAILQ_FOREACH(oid, &res->oids, val_entry) {
175
			snmpc_run(&sc, res->action, oid->val);
176
		}
177
	}
178
179
	close(sc.sc_fd);
180
}
181
182
void
183
snmpc_run(struct snmpc *sc, enum actions action, const char *oid)
184
{
185
	if (strcmp("all", oid) == 0)
186
		oid = SNMPC_OID_ALL;
187
188
	/*
189
	 * Set up the root OID and get the prefix length to shorten the
190
	 * printed OID strings of the children.
191
	 */
192
	if (smi_string2oid(oid, &sc->sc_oid) == -1)
193
		errx(1, "oid");
194
195
	bcopy(&sc->sc_oid, &sc->sc_root_oid, sizeof(sc->sc_root_oid));
196
	bcopy(&sc->sc_oid, &sc->sc_last_oid, sizeof(sc->sc_last_oid));
197
	if (sc->sc_oid.bo_n > 2)
198
		sc->sc_root_len = sc->sc_oid.bo_n - 1;
199
200
	sc->sc_nresp = 0;
201
202
	if (action == GET)
203
		snmpc_request(sc, SNMP_C_GETREQ);
204
	else if (action == BULKWALK)
205
		snmpc_request(sc, SNMP_C_GETBULKREQ);
206
	else
207
		snmpc_request(sc, SNMP_C_GETNEXTREQ);
208
}
209
210
void
211
snmpc_request(struct snmpc *sc, u_long type)
212
{
213
	struct pollfd		 pfd[1];
214
	int			 nfds, ret;
215
216
	/* Send SNMP request */
217
	if (snmpc_sendreq(sc, type) == -1)
218
		err(1, "request failed");
219
220
	/* Wait for response */
221
	pfd[0].fd = sc->sc_fd;
222
	pfd[0].events = POLLIN;
223
	nfds = poll(pfd, 1, 3 * 1000);
224
	if (nfds == -1 || (pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)))
225
		errx(1, "poll error");
226
	if (nfds == 0) {
227
		if (sc->sc_retry++ < sc->sc_retry_max) {
228
			warnx("time out, retry %d/%d",
229
			    sc->sc_retry, sc->sc_retry_max);
230
			snmpc_request(sc, type);
231
			return;
232
		}
233
		errx(1, "time out");
234
	}
235
	sc->sc_retry = 0;
236
237
	if ((ret = snmpc_response(sc)) != 0) {
238
		if (ret == -1)
239
			err(1, "response");
240
		return;
241
	}
242
243
	if (type == SNMP_C_GETREQ)
244
		return;
245
246
	snmpc_request(sc, type);
247
}
248
249
int
250
snmpc_response(struct snmpc *sc)
251
{
252
	char			 buf[BUFSIZ];
253
	struct ber_element	*resp = NULL, *s, *e;
254
	char			*value = NULL, *p;
255
	int			 ret = 0;
256
257
	/* Receive response */
258
	if (snmpc_recvresp(sc->sc_fd, sc->sc_version,
259
	    sc->sc_msgid, &resp) == -1)
260
		return (-1);
261
262
	if (ber_scanf_elements(resp, "{SS{SSSS{e}}", &s) != 0)
263
		goto fail;
264
265
	for (; s != NULL; s = s->be_next) {
266
		if (ber_scanf_elements(s, "{oe}", &sc->sc_oid, &e) != 0)
267
			goto fail;
268
269
		/* Break if the returned OID is not a child of the root. */
270
		if (sc->sc_nresp &&
271
		    (ber_oid_cmp(&sc->sc_root_oid, &sc->sc_oid) != 2 ||
272
		    e->be_class == BER_CLASS_CONTEXT ||
273
		    e->be_type == BER_TYPE_NULL)) {
274
			ret = 1;
275
			break;
276
		}
277
278
		if ((value = smi_print_element(e)) != NULL) {
279
			smi_oid2string(&sc->sc_oid, buf, sizeof(buf),
280
			    sc->sc_root_len);
281
			snmpc_display_hint(&sc->sc_oid, &value);
282
			p = buf;
283
			if (*p != '\0')
284
				printf("%s=%s\n", p, value);
285
			else
286
				printf("%s\n", value);
287
			free(value);
288
		}
289
		bcopy(&sc->sc_oid, &sc->sc_last_oid, sizeof(sc->sc_last_oid));
290
	}
291
292
	sc->sc_nresp++;
293
294
	ber_free_elements(resp);
295
	return (ret);
296
297
 fail:
298
	if (resp != NULL)
299
		ber_free_elements(resp);
300
	errno = EINVAL;
301
	return (-1);
302
}
303
304
void
305
snmpc_display_hint(struct ber_oid *oid, char **val)
306
{
307
	struct display_hint	*h;
308
	char			*newval;
309
310
	if (*val == NULL || strlen(*val) == 0)
311
		return;
312
313
	if ((h = snmpc_display_hint_lookup(oid)) == NULL)
314
		return;
315
	/* best-effort translation */
316
	if ((newval = h->print(*val)) == NULL)
317
		return;
318
319
	free(*val);
320
	*val = newval;
321
}
322
323
char *
324
snmpc_physaddress(char *v)
325
{
326
	char			 buf[ETHER_ADDR_LEN + 2];
327
	char			*str;
328
	ssize_t			 n;
329
330
	n = strnunvis(buf, v, ETHER_ADDR_LEN + 2);
331
	if (n == -1 || n != ETHER_ADDR_LEN + 2)
332
		return (NULL);
333
	if (asprintf(&str, "\"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\"",
334
	    buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]) == -1)
335
		return (NULL);
336
	return (str);
337
}
338
339
char *
340
snmpc_dateandtime(char *v)
341
{
342
	char			 buf[SNMPC_DATEANDTIME_LEN + 2];
343
	u_int16_t		 year;
344
	char			*str;
345
	ssize_t			 n;
346
347
	if ((n = strnunvis(buf, v, SNMPC_DATEANDTIME_LEN + 2)) == -1)
348
		return (NULL);
349
350
	bcopy(&buf[1], &year, sizeof(year));
351
	year = ntohs(year);
352
353
	switch (n) {
354
	case SNMPC_DATEANDTIME_SHORT_LEN + 2:
355
		if (asprintf(&str,
356
		    "\"%hu-%.02hhu-%.02hhu %.02hhu:%.02hhu:%.02hhu\"",
357
		    year, buf[3], buf[4], buf[5], buf[6], buf[7]) == -1)
358
			return (NULL);
359
		break;
360
	case SNMPC_DATEANDTIME_LEN + 2:
361
		if (asprintf(&str,
362
		    "\"%hu-%.02hhu-%.02hhu %.02hhu:%.02hhu:%.02hhu"
363
		    "%c%.02hhu%.02hhu\"",
364
		    year, buf[3], buf[4], buf[5], buf[6],
365
		    buf[7], buf[9], buf[10], buf[11]) == -1)
366
			return (NULL);
367
		break;
368
	default:
369
		return (NULL);
370
	}
371
372
	return (str);
373
}
374
375
struct display_hint *
376
snmpc_display_hint_lookup(struct ber_oid *oid)
377
{
378
	u_int			 i, j, found;
379
380
	for (i = 0; i < sizeof(display_hints) / sizeof(display_hints[0]); i++) {
381
		if (oid->bo_n < display_hints[i].oid.bo_n)
382
			continue;
383
		found = 1;
384
		for (j = 0; j < display_hints[i].oid.bo_n; j++) {
385
			if (oid->bo_id[j] != display_hints[i].oid.bo_id[j]) {
386
				found = 0;
387
				break;
388
			}
389
		}
390
391
		if (found)
392
			return (&display_hints[i]);
393
	}
394
	return (NULL);
395
}
396
397
int
398
snmpc_sendreq(struct snmpc *sc, u_long type)
399
{
400
	struct ber_element	*root, *b;
401
	struct ber		 ber;
402
	ssize_t			 len;
403
	u_int8_t		*ptr;
404
	int			 erroridx = 0;
405
406
	if (type == SNMP_C_GETBULKREQ)
407
		erroridx = SNMPC_MAXREPETITIONS;
408
409
	/* SNMP header */
410
	sc->sc_msgid = arc4random();
411
	if ((root = ber_add_sequence(NULL)) == NULL)
412
		return (-1);
413
	if ((b = ber_printf_elements(root, "ds{tddd{{O0}}",
414
	    sc->sc_version, sc->sc_community, BER_CLASS_CONTEXT, type,
415
	    sc->sc_msgid, 0, erroridx, &sc->sc_oid)) == NULL) {
416
		errno = EINVAL;
417
		goto fail;
418
	}
419
420
#ifdef DEBUG
421
	fprintf(stderr, "REQUEST(%lu):\n", type);
422
	smi_debug_elements(root);
423
#endif
424
425
	bzero(&ber, sizeof(ber));
426
	ber.fd = -1;
427
428
	len = ber_write_elements(&ber, root);
429
	if (ber_get_writebuf(&ber, (void *)&ptr) < 1)
430
		goto berfail;
431
432
	if (sendto(sc->sc_fd, ptr, len, 0,
433
	    (struct sockaddr *)&sc->sc_addr, sc->sc_addr_len) == -1)
434
		goto berfail;
435
436
	ber_free_elements(root);
437
	ber_free(&ber);
438
	return (0);
439
440
berfail:
441
	ber_free(&ber);
442
fail:
443
	ber_free_elements(root);
444
	return (-1);
445
}
446
447
int
448
snmpc_recvresp(int s, int msgver, u_int32_t msgid,
449
    struct ber_element **respptr)
450
{
451
	char			 buf[READ_BUF_SIZE];
452
	ssize_t			 rlen;
453
	struct ber		 ber;
454
	struct ber_element	*resp = NULL;
455
	char			*comn;
456
	long long		 ver, id;
457
458
	if ((rlen = recv(s, buf, sizeof(buf), MSG_WAITALL)) == -1)
459
		return (-1);
460
461
	bzero(&ber, sizeof(ber));
462
	ber.fd = -1;
463
	ber_set_application(&ber, smi_application);
464
	ber_set_readbuf(&ber, buf, rlen);
465
466
#ifdef DEBUG
467
	fprintf(stderr, "RESPONSE (%ld bytes):\n", rlen);
468
#endif
469
470
	resp = ber_read_elements(&ber, NULL);
471
	if (resp == NULL)
472
		goto fail;
473
474
#ifdef DEBUG
475
	smi_debug_elements(resp);
476
#endif
477
478
	if (ber_scanf_elements(resp, "{is{i", &ver, &comn, &id) != 0)
479
		goto fail;
480
	if (!(msgver == (int)ver && msgid == (u_int32_t)id))
481
		goto fail;
482
483
	*respptr = resp;
484
	return (0);
485
486
 fail:
487
	if (resp != NULL)
488
		ber_free_elements(resp);
489
	errno = EINVAL;
490
	return (-1);
491
}