xref: /freebsd/contrib/bsnmp/snmpd/action.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 /*
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  * Copyright (c) 2004-2006
6  *	Hartmut Brandt.
7  *	All rights reserved.
8  *
9  * Author: Harti Brandt <harti@freebsd.org>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Begemot: action.c 517 2006-10-31 08:52:04Z brandt_h $
33  *
34  * Variable access for SNMPd
35  */
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 #include <sys/sysctl.h>
39 #include <sys/un.h>
40 #include <sys/utsname.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <inttypes.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <syslog.h>
49 
50 #include "snmpmod.h"
51 #include "snmpd.h"
52 #include "tree.h"
53 #include "oid.h"
54 
55 static const struct asn_oid
56 	oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable;
57 
58 #ifdef __FreeBSD__
59 static const struct asn_oid
60 	oid_freeBSDVersion = OIDX_freeBSDVersion;
61 #endif
62 
63 /*
64  * Get an integer value from the KERN sysctl subtree.
65  */
66 static char *
67 act_getkernint(int id)
68 {
69 	int mib[2];
70 	size_t len;
71 	u_long value;
72 	char *string;
73 
74 	mib[0] = CTL_KERN;
75 	mib[1] = id;
76 	len = sizeof(value);
77 	if (sysctl(mib, 2, &value, &len, NULL, 0) != 0)
78 		return (NULL);
79 
80 	if ((string = malloc(20)) == NULL)
81 		return (NULL);
82 	sprintf(string, "%lu", value);
83 	return (string);
84 }
85 
86 /*
87  * Initialize global variables of the system group.
88  */
89 int
90 init_actvals(void)
91 {
92 	struct utsname uts;
93 	char *hostid;
94 	size_t len;
95 #ifdef __FreeBSD__
96 	char *rel, *p, *end;
97 	u_long num;
98 #endif
99 
100 	if (uname(&uts) == -1)
101 		return (-1);
102 
103 	if ((systemg.name = strdup(uts.nodename)) == NULL)
104 		return (-1);
105 
106 	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
107 		return (-1);
108 
109 	len = strlen(uts.nodename) + 1;
110 	len += strlen(hostid) + 1;
111 	len += strlen(uts.sysname) + 1;
112 	len += strlen(uts.release) + 1;
113 
114 	if ((systemg.descr = malloc(len)) == NULL) {
115 		free(hostid);
116 		return (-1);
117 	}
118 	sprintf(systemg.descr, "%s %s %s %s", uts.nodename, hostid, uts.sysname,
119 	    uts.release);
120 
121 #ifdef __FreeBSD__
122 	/*
123 	 * Construct a FreeBSD oid
124 	 */
125 	systemg.object_id = oid_freeBSDVersion;
126 	rel = uts.release;
127 	while ((p = strsep(&rel, ".")) != NULL &&
128 	    systemg.object_id.len < ASN_MAXOIDLEN) {
129 		systemg.object_id.subs[systemg.object_id.len] = 0;
130 		if (*p != '\0') {
131 			num = strtoul(p, &end, 10);
132 			if (end == p)
133 				break;
134 			systemg.object_id.subs[systemg.object_id.len] = num;
135 		}
136 		systemg.object_id.len++;
137 	}
138 #endif
139 
140 	free(hostid);
141 
142 	return (0);
143 }
144 
145 /*
146  * Initialize global variables of the snmpEngine group.
147  */
148 int
149 init_snmpd_engine(void)
150 {
151 	char *hostid;
152 
153 	snmpd_engine.engine_boots = 1;
154 	snmpd_engine.engine_time = 1;
155 	snmpd_engine.max_msg_size = 1500; /* XXX */
156 
157 	snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80;
158 	snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16;
159 	snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8;
160 	snmpd_engine.engine_id[3] = OID_freeBSD & 0xff;
161 	snmpd_engine.engine_id[4] = 128;
162 	snmpd_engine.engine_len = 5;
163 
164 	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
165 		return (-1);
166 
167 	if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) {
168 		memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
169 		    hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len);
170 		snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ;
171 	} else {
172 		memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
173 		    hostid, strlen(hostid));
174 		snmpd_engine.engine_len += strlen(hostid);
175 	}
176 
177 	free(hostid);
178 
179 	return (0);
180 }
181 
182 int
183 set_snmpd_engine(void)
184 {
185 	FILE *fp;
186 	uint32_t i;
187 	uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
188 	uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2];
189 
190 	if (engine_file[0] == '\0')
191 		return (-1);
192 
193 	cptr = myengine;
194 	for (i = 0; i < snmpd_engine.engine_len; i++)
195 		cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]);
196 	*cptr++ = '\n';
197 	*cptr++ = '\0';
198 
199 	if ((fp = fopen(engine_file, "r+")) != NULL) {
200 		if (fgets(engine, sizeof(engine) - 1, fp) == NULL ||
201 		    fscanf(fp, "%u",  &snmpd_engine.engine_boots) <= 0) {
202 			fclose(fp);
203 			goto save_boots;
204 		}
205 
206 		fclose(fp);
207 		if (strcmp(myengine, engine) != 0)
208 			snmpd_engine.engine_boots = 1;
209 		else
210 			snmpd_engine.engine_boots++;
211 	} else if (errno != ENOENT)
212 		return (-1);
213 
214 save_boots:
215 	if ((fp = fopen(engine_file, "w+")) == NULL)
216 		return (-1);
217 	fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots);
218 	fclose(fp);
219 
220 	return (0);
221 }
222 
223 void
224 update_snmpd_engine_time(void)
225 {
226 	uint64_t etime;
227 
228 	etime = (get_ticks() - start_tick) / 100ULL;
229 	if (etime < INT32_MAX)
230 		snmpd_engine.engine_time = etime;
231 	else {
232 		start_tick = get_ticks();
233 		(void)set_snmpd_engine();
234 		snmpd_engine.engine_time = start_tick;
235 	}
236 }
237 
238 /*************************************************************
239  *
240  * System group
241  */
242 int
243 op_system_group(struct snmp_context *ctx, struct snmp_value *value,
244     u_int sub, u_int iidx __unused, enum snmp_op op)
245 {
246 	asn_subid_t which = value->var.subs[sub - 1];
247 
248 	switch (op) {
249 
250 	  case SNMP_OP_GETNEXT:
251 		abort();
252 
253 	  case SNMP_OP_GET:
254 		break;
255 
256 	  case SNMP_OP_SET:
257 		switch (which) {
258 
259 		  case LEAF_sysDescr:
260 			if (community != COMM_INITIALIZE)
261 				return (SNMP_ERR_NOT_WRITEABLE);
262 			return (string_save(value, ctx, -1, &systemg.descr));
263 
264 		  case LEAF_sysObjectId:
265 			if (community != COMM_INITIALIZE)
266 				return (SNMP_ERR_NOT_WRITEABLE);
267 			return (oid_save(value, ctx, &systemg.object_id));
268 
269 		  case LEAF_sysContact:
270 			return (string_save(value, ctx, -1, &systemg.contact));
271 
272 		  case LEAF_sysName:
273 			return (string_save(value, ctx, -1, &systemg.name));
274 
275 		  case LEAF_sysLocation:
276 			return (string_save(value, ctx, -1, &systemg.location));
277 		}
278 		return (SNMP_ERR_NO_CREATION);
279 
280 	  case SNMP_OP_ROLLBACK:
281 		switch (which) {
282 
283 		  case LEAF_sysDescr:
284 			string_rollback(ctx, &systemg.descr);
285 			return (SNMP_ERR_NOERROR);
286 		  case LEAF_sysObjectId:
287 			oid_rollback(ctx, &systemg.object_id);
288 			return (SNMP_ERR_NOERROR);
289 		  case LEAF_sysContact:
290 			string_rollback(ctx, &systemg.contact);
291 			return (SNMP_ERR_NOERROR);
292 		  case LEAF_sysName:
293 			string_rollback(ctx, &systemg.name);
294 			return (SNMP_ERR_NOERROR);
295 		  case LEAF_sysLocation:
296 			string_rollback(ctx, &systemg.location);
297 			return (SNMP_ERR_NOERROR);
298 		}
299 		abort();
300 
301 	  case SNMP_OP_COMMIT:
302 		switch (which) {
303 
304 		  case LEAF_sysDescr:
305 			string_commit(ctx);
306 			return (SNMP_ERR_NOERROR);
307 		  case LEAF_sysObjectId:
308 			oid_commit(ctx);
309 			return (SNMP_ERR_NOERROR);
310 		  case LEAF_sysContact:
311 			string_commit(ctx);
312 			return (SNMP_ERR_NOERROR);
313 		  case LEAF_sysName:
314 			string_commit(ctx);
315 			return (SNMP_ERR_NOERROR);
316 		  case LEAF_sysLocation:
317 			string_commit(ctx);
318 			return (SNMP_ERR_NOERROR);
319 		}
320 		abort();
321 	}
322 
323 	/*
324 	 * Come here for GET.
325 	 */
326 	switch (which) {
327 
328 	  case LEAF_sysDescr:
329 		return (string_get(value, systemg.descr, -1));
330 	  case LEAF_sysObjectId:
331 		return (oid_get(value, &systemg.object_id));
332 	  case LEAF_sysUpTime:
333 		value->v.uint32 = get_ticks() - start_tick;
334 		break;
335 	  case LEAF_sysContact:
336 		return (string_get(value, systemg.contact, -1));
337 	  case LEAF_sysName:
338 		return (string_get(value, systemg.name, -1));
339 	  case LEAF_sysLocation:
340 		return (string_get(value, systemg.location, -1));
341 	  case LEAF_sysServices:
342 		value->v.integer = systemg.services;
343 		break;
344 	  case LEAF_sysORLastChange:
345 		value->v.uint32 = systemg.or_last_change;
346 		break;
347 	}
348 	return (SNMP_ERR_NOERROR);
349 }
350 
351 /*************************************************************
352  *
353  * Debug group
354  */
355 int
356 op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub,
357     u_int iidx __unused, enum snmp_op op)
358 {
359 	asn_subid_t which = value->var.subs[sub - 1];
360 
361 	switch (op) {
362 
363 	  case SNMP_OP_GETNEXT:
364 		abort();
365 
366 	  case SNMP_OP_GET:
367 		switch (which) {
368 
369 		  case LEAF_begemotSnmpdDebugDumpPdus:
370 			value->v.integer = TRUTH_MK(debug.dump_pdus);
371 			break;
372 
373 		  case LEAF_begemotSnmpdDebugSnmpTrace:
374 			value->v.uint32 = snmp_trace;
375 			break;
376 
377 		  case LEAF_begemotSnmpdDebugSyslogPri:
378 			value->v.integer = debug.logpri;
379 			break;
380 		}
381 		return (SNMP_ERR_NOERROR);
382 
383 	  case SNMP_OP_SET:
384 		switch (which) {
385 
386 		  case LEAF_begemotSnmpdDebugDumpPdus:
387 			if (!TRUTH_OK(value->v.integer))
388 				return (SNMP_ERR_WRONG_VALUE);
389 			ctx->scratch->int1 = debug.dump_pdus;
390 			debug.dump_pdus = TRUTH_GET(value->v.integer);
391 			return (SNMP_ERR_NOERROR);
392 
393 		  case LEAF_begemotSnmpdDebugSnmpTrace:
394 			ctx->scratch->int1 = snmp_trace;
395 			snmp_trace = value->v.uint32;
396 			return (SNMP_ERR_NOERROR);
397 
398 		  case LEAF_begemotSnmpdDebugSyslogPri:
399 			if (value->v.integer < 0 || value->v.integer > 8)
400 				return (SNMP_ERR_WRONG_VALUE);
401 			ctx->scratch->int1 = debug.logpri;
402 			debug.logpri = (u_int)value->v.integer;
403 			return (SNMP_ERR_NOERROR);
404 		}
405 		return (SNMP_ERR_NO_CREATION);
406 
407 	  case SNMP_OP_ROLLBACK:
408 		switch (which) {
409 
410 		  case LEAF_begemotSnmpdDebugDumpPdus:
411 			debug.dump_pdus = ctx->scratch->int1;
412 			return (SNMP_ERR_NOERROR);
413 
414 		  case LEAF_begemotSnmpdDebugSnmpTrace:
415 			snmp_trace = ctx->scratch->int1;
416 			return (SNMP_ERR_NOERROR);
417 
418 		  case LEAF_begemotSnmpdDebugSyslogPri:
419 			debug.logpri = ctx->scratch->int1;
420 			return (SNMP_ERR_NOERROR);
421 		}
422 		abort();
423 
424 	  case SNMP_OP_COMMIT:
425 		switch (which) {
426 
427 		  case LEAF_begemotSnmpdDebugDumpPdus:
428 		  case LEAF_begemotSnmpdDebugSnmpTrace:
429 			return (SNMP_ERR_NOERROR);
430 
431 		  case LEAF_begemotSnmpdDebugSyslogPri:
432 			if (debug.logpri == 0)
433 				setlogmask(0);
434 			else
435 				setlogmask(LOG_UPTO(debug.logpri - 1));
436 			return (SNMP_ERR_NOERROR);
437 		}
438 		abort();
439 	}
440 	abort();
441 }
442 
443 /*************************************************************
444  *
445  * OR Table
446  */
447 int
448 op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value,
449     u_int sub, u_int iidx __unused, enum snmp_op op)
450 {
451 	struct objres *objres;
452 
453 	switch (op) {
454 
455 	  case SNMP_OP_GETNEXT:
456 		if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub))
457 		    == NULL)
458 			return (SNMP_ERR_NOSUCHNAME);
459 		value->var.subs[sub] = objres->index;
460 		value->var.len = sub + 1;
461 		break;
462 
463 	  case SNMP_OP_GET:
464 		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
465 		    == NULL)
466 			return (SNMP_ERR_NOSUCHNAME);
467 		break;
468 
469 	  case SNMP_OP_SET:
470 		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
471 		    == NULL)
472 			return (SNMP_ERR_NO_CREATION);
473 		return (SNMP_ERR_NOT_WRITEABLE);
474 
475 	  case SNMP_OP_ROLLBACK:
476 	  case SNMP_OP_COMMIT:
477 	  default:
478 		abort();
479 	}
480 
481 	/*
482 	 * Come here for GET, GETNEXT.
483 	 */
484 	switch (value->var.subs[sub - 1]) {
485 
486 	  case LEAF_sysORID:
487 		value->v.oid = objres->oid;
488 		break;
489 
490 	  case LEAF_sysORDescr:
491 		return (string_get(value, objres->descr, -1));
492 
493 	  case LEAF_sysORUpTime:
494 		value->v.uint32 = objres->uptime;
495 		break;
496 	}
497 	return (SNMP_ERR_NOERROR);
498 }
499 
500 /*************************************************************
501  *
502  * mib-2 snmp
503  */
504 int
505 op_snmp(struct snmp_context *ctx, struct snmp_value *value,
506     u_int sub, u_int iidx __unused, enum snmp_op op)
507 {
508 	switch (op) {
509 
510 	  case SNMP_OP_GETNEXT:
511 		abort();
512 
513 	  case SNMP_OP_GET:
514 		switch (value->var.subs[sub - 1]) {
515 
516 		  case LEAF_snmpInPkts:
517 			value->v.uint32 = snmpd_stats.inPkts;
518 			break;
519 
520 		  case LEAF_snmpInBadVersions:
521 			value->v.uint32 = snmpd_stats.inBadVersions;
522 			break;
523 
524 		  case LEAF_snmpInBadCommunityNames:
525 			value->v.uint32 = snmpd_stats.inBadCommunityNames;
526 			break;
527 
528 		  case LEAF_snmpInBadCommunityUses:
529 			value->v.uint32 = snmpd_stats.inBadCommunityUses;
530 			break;
531 
532 		  case LEAF_snmpInASNParseErrs:
533 			value->v.uint32 = snmpd_stats.inASNParseErrs;
534 			break;
535 
536 		  case LEAF_snmpEnableAuthenTraps:
537 			value->v.integer = TRUTH_MK(snmpd.auth_traps);
538 			break;
539 
540 		  case LEAF_snmpSilentDrops:
541 			value->v.uint32 = snmpd_stats.silentDrops;
542 			break;
543 
544 		  case LEAF_snmpProxyDrops:
545 			value->v.uint32 = snmpd_stats.proxyDrops;
546 			break;
547 
548 		  default:
549 			return (SNMP_ERR_NOSUCHNAME);
550 
551 		}
552 		return (SNMP_ERR_NOERROR);
553 
554 	  case SNMP_OP_SET:
555 		switch (value->var.subs[sub - 1]) {
556 		  case LEAF_snmpEnableAuthenTraps:
557 			if (!TRUTH_OK(value->v.integer))
558 				return (SNMP_ERR_WRONG_VALUE);
559 			ctx->scratch->int1 = value->v.integer;
560 			snmpd.auth_traps = TRUTH_GET(value->v.integer);
561 			return (SNMP_ERR_NOERROR);
562 		}
563 		abort();
564 
565 	  case SNMP_OP_ROLLBACK:
566 		switch (value->var.subs[sub - 1]) {
567 		  case LEAF_snmpEnableAuthenTraps:
568 			snmpd.auth_traps = ctx->scratch->int1;
569 			return (SNMP_ERR_NOERROR);
570 		}
571 		abort();
572 
573 	  case SNMP_OP_COMMIT:
574 		switch (value->var.subs[sub - 1]) {
575 		  case LEAF_snmpEnableAuthenTraps:
576 			return (SNMP_ERR_NOERROR);
577 		}
578 		abort();
579 	}
580 	abort();
581 }
582 
583 /*************************************************************
584  *
585  * SNMPd statistics group
586  */
587 int
588 op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
589     u_int sub, u_int iidx __unused, enum snmp_op op)
590 {
591 	switch (op) {
592 
593 	  case SNMP_OP_GET:
594 		switch (value->var.subs[sub - 1]) {
595 
596 		  case LEAF_begemotSnmpdStatsNoRxBufs:
597 			value->v.uint32 = snmpd_stats.noRxbuf;
598 			break;
599 
600 		  case LEAF_begemotSnmpdStatsNoTxBufs:
601 			value->v.uint32 = snmpd_stats.noTxbuf;
602 			break;
603 
604 		  case LEAF_begemotSnmpdStatsInTooLongPkts:
605 			value->v.uint32 = snmpd_stats.inTooLong;
606 			break;
607 
608 		  case LEAF_begemotSnmpdStatsInBadPduTypes:
609 			value->v.uint32 = snmpd_stats.inBadPduTypes;
610 			break;
611 
612 		  default:
613 			return (SNMP_ERR_NOSUCHNAME);
614 		}
615 		return (SNMP_ERR_NOERROR);
616 
617 	  case SNMP_OP_SET:
618 	  case SNMP_OP_ROLLBACK:
619 	  case SNMP_OP_COMMIT:
620 	  case SNMP_OP_GETNEXT:
621 		abort();
622 	}
623 	abort();
624 }
625 
626 /*
627  * SNMPd configuration scalars
628  */
629 int
630 op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value,
631     u_int sub, u_int iidx __unused, enum snmp_op op)
632 {
633 	asn_subid_t which = value->var.subs[sub - 1];
634 
635 	switch (op) {
636 
637 	  case SNMP_OP_GETNEXT:
638 		abort();
639 
640 	  case SNMP_OP_GET:
641 		switch (which) {
642 
643 		  case LEAF_begemotSnmpdTransmitBuffer:
644 			value->v.integer = snmpd.txbuf;
645 			break;
646 		  case LEAF_begemotSnmpdReceiveBuffer:
647 			value->v.integer = snmpd.rxbuf;
648 			break;
649 		  case LEAF_begemotSnmpdCommunityDisable:
650 			value->v.integer = TRUTH_MK(snmpd.comm_dis);
651 			break;
652 		  case LEAF_begemotSnmpdTrap1Addr:
653 			return (ip_get(value, snmpd.trap1addr));
654 		  case LEAF_begemotSnmpdVersionEnable:
655 			value->v.uint32 = snmpd.version_enable;
656 			break;
657 		  default:
658 			return (SNMP_ERR_NOSUCHNAME);
659 		}
660 		return (SNMP_ERR_NOERROR);
661 
662 	  case SNMP_OP_SET:
663 		switch (which) {
664 
665 		  case LEAF_begemotSnmpdTransmitBuffer:
666 			ctx->scratch->int1 = snmpd.txbuf;
667 			if (value->v.integer < 484 ||
668 			    value->v.integer > 65535)
669 				return (SNMP_ERR_WRONG_VALUE);
670 			snmpd.txbuf = value->v.integer;
671 			return (SNMP_ERR_NOERROR);
672 
673 		  case LEAF_begemotSnmpdReceiveBuffer:
674 			ctx->scratch->int1 = snmpd.rxbuf;
675 			if (value->v.integer < 484 ||
676 			    value->v.integer > 65535)
677 				return (SNMP_ERR_WRONG_VALUE);
678 			snmpd.rxbuf = value->v.integer;
679 			return (SNMP_ERR_NOERROR);
680 
681 		  case LEAF_begemotSnmpdCommunityDisable:
682 			ctx->scratch->int1 = snmpd.comm_dis;
683 			if (!TRUTH_OK(value->v.integer))
684 				return (SNMP_ERR_WRONG_VALUE);
685 			if (TRUTH_GET(value->v.integer)) {
686 				snmpd.comm_dis = 1;
687 			} else {
688 				if (snmpd.comm_dis)
689 					return (SNMP_ERR_WRONG_VALUE);
690 			}
691 			return (SNMP_ERR_NOERROR);
692 
693 		  case LEAF_begemotSnmpdTrap1Addr:
694 			return (ip_save(value, ctx, snmpd.trap1addr));
695 
696 		  case LEAF_begemotSnmpdVersionEnable:
697 			if (community != COMM_INITIALIZE)
698 				return (SNMP_ERR_NOT_WRITEABLE);
699 			ctx->scratch->int1 = snmpd.version_enable;
700 			if (value->v.uint32 == 0 ||
701 			    (value->v.uint32 & ~VERS_ENABLE_ALL))
702 				return (SNMP_ERR_WRONG_VALUE);
703 			snmpd.version_enable = value->v.uint32;
704 			return (SNMP_ERR_NOERROR);
705 		}
706 		abort();
707 
708 	  case SNMP_OP_ROLLBACK:
709 		switch (which) {
710 
711 		  case LEAF_begemotSnmpdTransmitBuffer:
712 			snmpd.rxbuf = ctx->scratch->int1;
713 			return (SNMP_ERR_NOERROR);
714 		  case LEAF_begemotSnmpdReceiveBuffer:
715 			snmpd.txbuf = ctx->scratch->int1;
716 			return (SNMP_ERR_NOERROR);
717 		  case LEAF_begemotSnmpdCommunityDisable:
718 			snmpd.comm_dis = ctx->scratch->int1;
719 			return (SNMP_ERR_NOERROR);
720 		  case LEAF_begemotSnmpdTrap1Addr:
721 			ip_rollback(ctx, snmpd.trap1addr);
722 			return (SNMP_ERR_NOERROR);
723 		  case LEAF_begemotSnmpdVersionEnable:
724 			snmpd.version_enable = ctx->scratch->int1;
725 			return (SNMP_ERR_NOERROR);
726 		}
727 		abort();
728 
729 	  case SNMP_OP_COMMIT:
730 		switch (which) {
731 
732 		  case LEAF_begemotSnmpdTransmitBuffer:
733 		  case LEAF_begemotSnmpdReceiveBuffer:
734 		  case LEAF_begemotSnmpdCommunityDisable:
735 			return (SNMP_ERR_NOERROR);
736 		  case LEAF_begemotSnmpdTrap1Addr:
737 			ip_commit(ctx);
738 			return (SNMP_ERR_NOERROR);
739 		  case LEAF_begemotSnmpdVersionEnable:
740 			return (SNMP_ERR_NOERROR);
741 		}
742 		abort();
743 	}
744 	abort();
745 }
746 
747 /*
748  * The community table
749  */
750 int
751 op_community(struct snmp_context *ctx, struct snmp_value *value,
752     u_int sub, u_int iidx __unused, enum snmp_op op)
753 {
754 	struct asn_oid idx;
755 	struct community *c;
756 	asn_subid_t which = value->var.subs[sub - 1];
757 
758 	switch (op) {
759 
760 	  case SNMP_OP_GETNEXT:
761 		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
762 		    (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
763 			return (SNMP_ERR_NOSUCHNAME);
764 		index_append(&value->var, sub, &c->index);
765 		break;
766 
767 	  case SNMP_OP_GET:
768 		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
769 		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
770 			return (SNMP_ERR_NOSUCHNAME);
771 		break;
772 
773 	  case SNMP_OP_SET:
774 		if (community != COMM_INITIALIZE && snmpd.comm_dis)
775 			return (SNMP_ERR_NOT_WRITEABLE);
776 		idx.len = 2;
777 		idx.subs[0] = 0;
778 		idx.subs[1] = value->var.subs[value->var.len - 1];
779 		switch (which) {
780 		case LEAF_begemotSnmpdCommunityString:
781 			/* check that given string is unique */
782 			TAILQ_FOREACH(c, &community_list, link) {
783 				if (!asn_compare_oid(&idx, &c->index))
784 					continue;
785 				if (c->string != NULL && strcmp(c->string,
786 				    value->v.octetstring.octets) == 0)
787 					return (SNMP_ERR_WRONG_VALUE);
788 			}
789 		case LEAF_begemotSnmpdCommunityPermission:
790 			break;
791 		default:
792 			return (SNMP_ERR_NOT_WRITEABLE);
793 		}
794 		if ((c = FIND_OBJECT_OID(&community_list, &value->var,
795 		    sub)) == NULL) {
796 			/* create new community and use user sepcified index */
797 			c = comm_define_ordered(COMM_READ, "SNMP Custom Community",
798 			    &idx, NULL, NULL);
799 			if (c == NULL)
800 				return (SNMP_ERR_NO_CREATION);
801 		}
802 		switch (which) {
803 		case LEAF_begemotSnmpdCommunityString:
804 			return (string_save(value, ctx, -1, &c->string));
805 		case LEAF_begemotSnmpdCommunityPermission:
806 			if (value->v.integer != COMM_READ &&
807 			    value->v.integer != COMM_WRITE)
808 				return (SNMP_ERR_WRONG_VALUE);
809 			c->private = value->v.integer;
810 			break;
811 		default:
812 			return (SNMP_ERR_NOT_WRITEABLE);
813 		}
814 		return (SNMP_ERR_NOERROR);
815 
816 	  case SNMP_OP_ROLLBACK:
817 		if (which == LEAF_begemotSnmpdCommunityString) {
818 			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
819 			    sub)) == NULL)
820 				string_free(ctx);
821 			else
822 				string_rollback(ctx, &c->string);
823 			return (SNMP_ERR_NOERROR);
824 		}
825 		if (which == LEAF_begemotSnmpdCommunityPermission)
826 			return (SNMP_ERR_NOERROR);
827 		abort();
828 
829 	  case SNMP_OP_COMMIT:
830 		if (which == LEAF_begemotSnmpdCommunityString) {
831 			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
832 			    sub)) == NULL)
833 				string_free(ctx);
834 			else
835 				string_commit(ctx);
836 			return (SNMP_ERR_NOERROR);
837 		}
838 		if (which == LEAF_begemotSnmpdCommunityPermission)
839 			return (SNMP_ERR_NOERROR);
840 		abort();
841 
842 	  default:
843 		abort();
844 	}
845 
846 	switch (which) {
847 
848 	  case LEAF_begemotSnmpdCommunityString:
849 		return (string_get(value, c->string, -1));
850 
851 	  case LEAF_begemotSnmpdCommunityDescr:
852 		return (string_get(value, c->descr, -1));
853 
854 	  case LEAF_begemotSnmpdCommunityPermission:
855 		value->v.integer = c->private;
856 		return (SNMP_ERR_NOERROR);
857 	  default:
858 		return (SNMP_ERR_NOT_WRITEABLE);
859 	}
860 	abort();
861 }
862 
863 /*
864  * Module table.
865  */
866 struct module_dep {
867 	struct snmp_dependency dep;
868 	u_char	section[LM_SECTION_MAX + 1];
869 	u_char	*path;
870 	struct lmodule *m;
871 };
872 
873 static int
874 dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep,
875     enum snmp_depop op)
876 {
877 	struct module_dep *mdep = (struct module_dep *)(void *)dep;
878 
879 	switch (op) {
880 
881 	  case SNMP_DEPOP_COMMIT:
882 		if (mdep->path == NULL) {
883 			/* unload - find the module */
884 			TAILQ_FOREACH(mdep->m, &lmodules, link)
885 				if (strcmp(mdep->m->section,
886 				    mdep->section) == 0)
887 					break;
888 			if (mdep->m == NULL)
889 				/* no such module - that's ok */
890 				return (SNMP_ERR_NOERROR);
891 
892 			/* handle unloading in the finalizer */
893 			return (SNMP_ERR_NOERROR);
894 		}
895 		/* load */
896 		if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL) {
897 			/* could not load */
898 			return (SNMP_ERR_RES_UNAVAIL);
899 		}
900 		/* start in finalizer */
901 		return (SNMP_ERR_NOERROR);
902 
903 	  case SNMP_DEPOP_ROLLBACK:
904 		if (mdep->path == NULL) {
905 			/* rollback unload - the finalizer takes care */
906 			return (SNMP_ERR_NOERROR);
907 		}
908 		/* rollback load */
909 		lm_unload(mdep->m);
910 		return (SNMP_ERR_NOERROR);
911 
912 	  case SNMP_DEPOP_FINISH:
913 		if (mdep->path == NULL) {
914 			if (mdep->m != NULL && ctx->code == SNMP_RET_OK)
915 				lm_unload(mdep->m);
916 		} else {
917 			if (mdep->m != NULL && ctx->code == SNMP_RET_OK &&
918 			    community != COMM_INITIALIZE)
919 				lm_start(mdep->m);
920 			free(mdep->path);
921 		}
922 		return (SNMP_ERR_NOERROR);
923 	}
924 	abort();
925 }
926 
927 int
928 op_modules(struct snmp_context *ctx, struct snmp_value *value,
929     u_int sub, u_int iidx, enum snmp_op op)
930 {
931 	asn_subid_t which = value->var.subs[sub - 1];
932 	struct lmodule *m;
933 	u_char *section, *ptr;
934 	size_t seclen;
935 	struct module_dep *mdep;
936 	struct asn_oid idx;
937 
938 	switch (op) {
939 
940 	  case SNMP_OP_GETNEXT:
941 		if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
942 			return (SNMP_ERR_NOSUCHNAME);
943 		index_append(&value->var, sub, &m->index);
944 		break;
945 
946 	  case SNMP_OP_GET:
947 		if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
948 			return (SNMP_ERR_NOSUCHNAME);
949 		break;
950 
951 	  case SNMP_OP_SET:
952 		m = FIND_OBJECT_OID(&lmodules, &value->var, sub);
953 		if (which != LEAF_begemotSnmpdModulePath) {
954 			if (m == NULL)
955 				return (SNMP_ERR_NO_CREATION);
956 			return (SNMP_ERR_NOT_WRITEABLE);
957 		}
958 
959 		/* the errors in the next few statements can only happen when
960 		 * m is NULL, hence the NO_CREATION error. */
961 		if (index_decode(&value->var, sub, iidx,
962 		    &section, &seclen))
963 			return (SNMP_ERR_NO_CREATION);
964 
965 		/* check the section name */
966 		if (seclen > LM_SECTION_MAX || seclen == 0) {
967 			free(section);
968 			return (SNMP_ERR_NO_CREATION);
969 		}
970 		for (ptr = section; ptr < section + seclen; ptr++)
971 			if (!isascii(*ptr) || !isalnum(*ptr)) {
972 				free(section);
973 				return (SNMP_ERR_NO_CREATION);
974 			}
975 		if (!isalpha(section[0])) {
976 			free(section);
977 			return (SNMP_ERR_NO_CREATION);
978 		}
979 
980 		/* check the path */
981 		for (ptr = value->v.octetstring.octets;
982 		     ptr < value->v.octetstring.octets + value->v.octetstring.len;
983 		     ptr++) {
984 			if (*ptr == '\0') {
985 				free(section);
986 				return (SNMP_ERR_WRONG_VALUE);
987 			}
988 		}
989 
990 		if (m == NULL) {
991 			if (value->v.octetstring.len == 0) {
992 				free(section);
993 				return (SNMP_ERR_INCONS_VALUE);
994 			}
995 		} else {
996 			if (value->v.octetstring.len != 0) {
997 				free(section);
998 				return (SNMP_ERR_INCONS_VALUE);
999 			}
1000 		}
1001 
1002 		asn_slice_oid(&idx, &value->var, sub, value->var.len);
1003 
1004 		/* so far, so good */
1005 		mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx,
1006 		    &oid_begemotSnmpdModuleTable, &idx,
1007 		    sizeof(*mdep), dep_modules);
1008 		if (mdep == NULL) {
1009 			free(section);
1010 			return (SNMP_ERR_RES_UNAVAIL);
1011 		}
1012 
1013 		if (mdep->section[0] != '\0') {
1014 			/* two writes to the same entry - bad */
1015 			free(section);
1016 			return (SNMP_ERR_INCONS_VALUE);
1017 		}
1018 
1019 		strncpy(mdep->section, section, seclen);
1020 		mdep->section[seclen] = '\0';
1021 		free(section);
1022 
1023 		if (value->v.octetstring.len == 0)
1024 			mdep->path = NULL;
1025 		else {
1026 			if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL)
1027 				return (SNMP_ERR_RES_UNAVAIL);
1028 			strncpy(mdep->path, value->v.octetstring.octets,
1029 			    value->v.octetstring.len);
1030 			mdep->path[value->v.octetstring.len] = '\0';
1031 		}
1032 		ctx->scratch->ptr1 = mdep;
1033 		return (SNMP_ERR_NOERROR);
1034 
1035 	  case SNMP_OP_ROLLBACK:
1036 	  case SNMP_OP_COMMIT:
1037 		return (SNMP_ERR_NOERROR);
1038 
1039 	  default:
1040 		abort();
1041 	}
1042 
1043 	switch (which) {
1044 
1045 	  case LEAF_begemotSnmpdModulePath:
1046 		return (string_get(value, m->path, -1));
1047 
1048 	  case LEAF_begemotSnmpdModuleComment:
1049 		return (string_get(value, m->config->comment, -1));
1050 	}
1051 	abort();
1052 }
1053 
1054 int
1055 op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value,
1056     u_int sub, u_int iidx __unused, enum snmp_op op)
1057 {
1058 	switch (op) {
1059 
1060 	  case SNMP_OP_GETNEXT:
1061 		abort();
1062 
1063 	  case SNMP_OP_GET:
1064 		switch (value->var.subs[sub - 1]) {
1065 
1066 		  case LEAF_snmpSetSerialNo:
1067 			value->v.integer = snmp_serial_no;
1068 			break;
1069 
1070 		  default:
1071 			abort();
1072 		}
1073 		return (SNMP_ERR_NOERROR);
1074 
1075 	  case SNMP_OP_SET:
1076 		switch (value->var.subs[sub - 1]) {
1077 
1078 		  case LEAF_snmpSetSerialNo:
1079 			if (value->v.integer != snmp_serial_no)
1080 				return (SNMP_ERR_INCONS_VALUE);
1081 			break;
1082 
1083 		  default:
1084 			abort();
1085 		}
1086 		return (SNMP_ERR_NOERROR);
1087 
1088 	  case SNMP_OP_ROLLBACK:
1089 		return (SNMP_ERR_NOERROR);
1090 
1091 	  case SNMP_OP_COMMIT:
1092 		if (snmp_serial_no++ == 2147483647)
1093 			snmp_serial_no = 0;
1094 		return (SNMP_ERR_NOERROR);
1095 	}
1096 	abort();
1097 }
1098 
1099 /*
1100  * SNMP Engine
1101  */
1102 int
1103 op_snmp_engine(struct snmp_context *ctx __unused, struct snmp_value *value,
1104     u_int sub, u_int iidx __unused, enum snmp_op op)
1105 {
1106 	asn_subid_t which = value->var.subs[sub - 1];
1107 
1108 	switch (op) {
1109 	case SNMP_OP_GETNEXT:
1110 		abort();
1111 
1112 	case SNMP_OP_GET:
1113 		break;
1114 
1115 	case SNMP_OP_SET:
1116 		if (community != COMM_INITIALIZE)
1117 			return (SNMP_ERR_NOT_WRITEABLE);
1118 		switch (which) {
1119 		case LEAF_snmpEngineID:
1120 			if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ)
1121 				return (SNMP_ERR_WRONG_VALUE);
1122 			ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len);
1123 			if (ctx->scratch->ptr1 == NULL)
1124 				return (SNMP_ERR_GENERR);
1125 			memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id,
1126 			    snmpd_engine.engine_len);
1127 			ctx->scratch->int1 = snmpd_engine.engine_len;
1128 			snmpd_engine.engine_len = value->v.octetstring.len;
1129 			memcpy(snmpd_engine.engine_id,
1130 			    value->v.octetstring.octets,
1131 			    value->v.octetstring.len);
1132 			break;
1133 
1134 		case LEAF_snmpEngineMaxMessageSize:
1135 			ctx->scratch->int1 = snmpd_engine.max_msg_size;
1136 			snmpd_engine.max_msg_size = value->v.integer;
1137 			break;
1138 
1139 		default:
1140 			return (SNMP_ERR_NOT_WRITEABLE);
1141 		}
1142 		return (SNMP_ERR_NOERROR);
1143 
1144 	case SNMP_OP_ROLLBACK:
1145 		switch (which) {
1146 		case LEAF_snmpEngineID:
1147 			snmpd_engine.engine_len = ctx->scratch->int1;
1148 			memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1,
1149 			    snmpd_engine.engine_len);
1150 			free(ctx->scratch->ptr1);
1151 			break;
1152 
1153 		case LEAF_snmpEngineMaxMessageSize:
1154 			snmpd_engine.max_msg_size = ctx->scratch->int1;
1155 			break;
1156 
1157 		default:
1158 			abort();
1159 		}
1160 		return (SNMP_ERR_NOERROR);
1161 
1162 	case SNMP_OP_COMMIT:
1163 		if (which == LEAF_snmpEngineID) {
1164 			if (set_snmpd_engine() < 0) {
1165 				snmpd_engine.engine_len = ctx->scratch->int1;
1166 				memcpy(snmpd_engine.engine_id,
1167 				    ctx->scratch->ptr1, ctx->scratch->int1);
1168 			}
1169 			free(ctx->scratch->ptr1);
1170 		}
1171 		return (SNMP_ERR_NOERROR);
1172 	}
1173 
1174 
1175 	switch (which) {
1176 	case LEAF_snmpEngineID:
1177 		return (string_get(value, snmpd_engine.engine_id,
1178 		    snmpd_engine.engine_len));
1179 	case LEAF_snmpEngineBoots:
1180 		value->v.integer = snmpd_engine.engine_boots;
1181 		break;
1182 	case LEAF_snmpEngineTime:
1183 		update_snmpd_engine_time();
1184 		value->v.integer = snmpd_engine.engine_time;
1185 		break;
1186 	case LEAF_snmpEngineMaxMessageSize:
1187 		value->v.integer = snmpd_engine.max_msg_size;
1188 		break;
1189 	default:
1190 		return (SNMP_ERR_NOSUCHNAME);
1191 	}
1192 
1193 	return (SNMP_ERR_NOERROR);
1194 }
1195 
1196 /*
1197  * Transport table
1198  */
1199 int
1200 op_transport_table(struct snmp_context *ctx __unused, struct snmp_value *value,
1201     u_int sub, u_int iidx, enum snmp_op op)
1202 {
1203 	asn_subid_t which = value->var.subs[sub - 1];
1204 	struct transport *t;
1205 	u_char *tname, *ptr;
1206 	size_t tnamelen;
1207 
1208 	switch (op) {
1209 
1210 	  case SNMP_OP_GETNEXT:
1211 		if ((t = NEXT_OBJECT_OID(&transport_list, &value->var, sub))
1212 		    == NULL)
1213 			return (SNMP_ERR_NOSUCHNAME);
1214 		index_append(&value->var, sub, &t->index);
1215 		break;
1216 
1217 	  case SNMP_OP_GET:
1218 		if ((t = FIND_OBJECT_OID(&transport_list, &value->var, sub))
1219 		    == NULL)
1220 			return (SNMP_ERR_NOSUCHNAME);
1221 		break;
1222 
1223 	  case SNMP_OP_SET:
1224 		t = FIND_OBJECT_OID(&transport_list, &value->var, sub);
1225 		if (which != LEAF_begemotSnmpdTransportStatus) {
1226 			if (t == NULL)
1227 				return (SNMP_ERR_NO_CREATION);
1228 			return (SNMP_ERR_NOT_WRITEABLE);
1229 		}
1230 
1231 		/* the errors in the next few statements can only happen when
1232 		 * t is NULL, hence the NO_CREATION error. */
1233 		if (index_decode(&value->var, sub, iidx,
1234 		    &tname, &tnamelen))
1235 			return (SNMP_ERR_NO_CREATION);
1236 
1237 		/* check the section name */
1238 		if (tnamelen >= TRANS_NAMELEN || tnamelen == 0) {
1239 			free(tname);
1240 			return (SNMP_ERR_NO_CREATION);
1241 		}
1242 		for (ptr = tname; ptr < tname + tnamelen; ptr++) {
1243 			if (!isascii(*ptr) || !isalnum(*ptr)) {
1244 				free(tname);
1245 				return (SNMP_ERR_NO_CREATION);
1246 			}
1247 		}
1248 
1249 		/* for now */
1250 		return (SNMP_ERR_NOT_WRITEABLE);
1251 
1252 	  case SNMP_OP_ROLLBACK:
1253 	  case SNMP_OP_COMMIT:
1254 		return (SNMP_ERR_NOERROR);
1255 	  default:
1256 		abort();
1257 	}
1258 
1259 	switch (which) {
1260 
1261 	    case LEAF_begemotSnmpdTransportStatus:
1262 		value->v.integer = 1;
1263 		break;
1264 
1265 	    case LEAF_begemotSnmpdTransportOid:
1266 		memcpy(&value->v.oid, &t->vtab->id, sizeof(t->vtab->id));
1267 		break;
1268 	}
1269 	return (SNMP_ERR_NOERROR);
1270 }
1271