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
55static const struct asn_oid
56	oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable;
57
58#ifdef __FreeBSD__
59static const struct asn_oid
60	oid_freeBSDVersion = OIDX_freeBSDVersion;
61#endif
62
63/*
64 * Get an integer value from the KERN sysctl subtree.
65 */
66static char *
67act_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 */
89int
90init_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 */
148int
149init_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
182int
183set_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
214save_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
223void
224update_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 */
242int
243op_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 */
355int
356op_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 */
447int
448op_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 */
504int
505op_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 */
587int
588op_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 */
629int
630op_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 */
750int
751op_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 */
866struct 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
873static int
874dep_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
927int
928op_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
1054int
1055op_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 */
1102int
1103op_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 */
1199int
1200op_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