xref: /openbsd-src/usr.sbin/snmpd/trap.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: trap.c,v 1.22 2014/04/14 12:55:10 blambert Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 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/param.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/tree.h>
26 
27 #include <net/if.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <event.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <pwd.h>
39 
40 #include "snmpd.h"
41 #include "mib.h"
42 
43 extern struct snmpd	*env;
44 
45 void
46 trap_init(void)
47 {
48 	struct ber_oid	 trapoid = OID(MIB_coldStart);
49 
50 	/*
51 	 * Send a coldStart to notify that the daemon has been
52 	 * started and re-initialized.
53 	 */
54 	trap_send(&trapoid, NULL);
55 }
56 
57 int
58 trap_agentx(struct agentx_handle *h, struct agentx_pdu *pdu, int *idx,
59     char **varcpy, int *vcpylen)
60 {
61 	struct agentx_varbind_hdr	 vbhdr;
62 	struct ber_oid			 o, oid;
63 	struct ber_oid			 uptime = OID(MIB_sysUpTime);
64 	struct ber_oid			 trapoid = OID(MIB_snmpTrapOID);
65 	u_int32_t			 d;
66 	u_int64_t			 l;
67 	char				*str;
68 	int				 slen;
69 	struct ber_element		*a, *ber, *varbind;
70 	int				 x = 0, state = 0;
71 	int				 ret = AGENTX_ERR_NONE;
72 	int				 seensysuptime, seentrapoid;
73 	size_t				 len = 0;
74 	pid_t				 pid = -1;
75 	char				*v = NULL;
76 
77 	*varcpy = NULL;
78 	ber = varbind = NULL;
79 	seensysuptime = seentrapoid = 0;
80 
81 	if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) {
82 		ret = AGENTX_ERR_UNSUPPORTED_CONTEXT;
83 		goto done;
84 	}
85 
86 	if ((v = malloc(pdu->hdr->length)) == NULL ||
87 	    snmp_agentx_copy_raw(pdu, v, pdu->hdr->length) == -1) {
88 		ret = AGENTX_ERR_PROCESSING_ERROR;
89 		goto done;
90 	}
91 
92 	while (pdu->datalen > sizeof(struct agentx_hdr)) {
93 		x++;
94 
95 		if (snmp_agentx_read_vbhdr(pdu, &vbhdr) == -1 ||
96 		    snmp_agentx_read_oid(pdu, (struct snmp_oid *)&oid) == -1) {
97 			ret = AGENTX_ERR_PARSE_ERROR;
98 			goto done;
99 		}
100 		if (state < 2) {
101 			if (state == 0 && ber_oid_cmp(&oid, &uptime) == 0) {
102 				if (snmp_agentx_read_int(pdu, &d) == -1) {
103 					ret = AGENTX_ERR_PARSE_ERROR;
104 					goto done;
105 				}
106 				state = 1;
107 				continue;
108 			} else if (ber_oid_cmp(&oid, &trapoid) == 0) {
109 				if (snmp_agentx_read_oid(pdu,
110 				    (struct snmp_oid *)&o) == -1) {
111 					ret = AGENTX_ERR_PARSE_ERROR;
112 					goto done;
113 				}
114 				state = 2;
115 				continue;
116 			} else {
117 				ret = AGENTX_ERR_PROCESSING_ERROR;
118 				goto done;
119 			}
120 		}
121 
122 		ber = ber_add_sequence(ber);
123 		if (varbind == NULL)
124 		varbind = ber;
125 
126 		a = ber_add_oid(ber, &oid);
127 
128 		switch (vbhdr.type) {
129 		case AGENTX_NO_SUCH_OBJECT:
130 		case AGENTX_NO_SUCH_INSTANCE:
131 		case AGENTX_END_OF_MIB_VIEW:
132 		case AGENTX_NULL:
133 			a = ber_add_null(a);
134 			break;
135 
136 		case AGENTX_IP_ADDRESS:
137 		case AGENTX_OPAQUE:
138 		case AGENTX_OCTET_STRING:
139 			str = snmp_agentx_read_octetstr(pdu, &slen);
140 			if (str == NULL) {
141 				ret = AGENTX_ERR_PARSE_ERROR;
142 				goto done;
143 			}
144 			a = ber_add_nstring(a, str, slen);
145 			break;
146 
147 		case AGENTX_OBJECT_IDENTIFIER:
148 			if (snmp_agentx_read_oid(pdu,
149 			    (struct snmp_oid *)&oid) == -1) {
150 				ret = AGENTX_ERR_PARSE_ERROR;
151 				goto done;
152 			}
153 			a = ber_add_oid(a, &oid);
154 			break;
155 
156 		case AGENTX_INTEGER:
157 		case AGENTX_COUNTER32:
158 		case AGENTX_GAUGE32:
159 		case AGENTX_TIME_TICKS:
160 			if (snmp_agentx_read_int(pdu, &d) == -1) {
161 				ret = AGENTX_ERR_PARSE_ERROR;
162 				goto done;
163 			}
164 			a = ber_add_integer(a, d);
165 			break;
166 
167 		case AGENTX_COUNTER64:
168 			if (snmp_agentx_read_int64(pdu, &l) == -1) {
169 				ret = AGENTX_ERR_PARSE_ERROR;
170 				goto done;
171 			}
172 			a = ber_add_integer(a, l);
173 			break;
174 
175 		default:
176 			log_debug("unknown data type '%i'", vbhdr.type);
177 			ret = AGENTX_ERR_PARSE_ERROR;
178 			goto done;
179 		}
180 
181 		/* AgentX types correspond to BER types */
182 		switch (vbhdr.type) {
183 		case BER_TYPE_INTEGER:
184 		case BER_TYPE_BITSTRING:
185 		case BER_TYPE_OCTETSTRING:
186 		case BER_TYPE_NULL:
187 		case BER_TYPE_OBJECT:
188 			/* universal types */
189 			break;
190 		default:
191 			/* application-specific types */
192 			ber_set_header(a, BER_CLASS_APPLICATION, vbhdr.type);
193 			break;
194 		}
195 	}
196 
197 	if (varbind != NULL)
198 		len = ber_calc_len(varbind);
199 	log_debug("trap_agentx: from pid %u len %d elements %d",
200 	    pid, len, x);
201 
202 	trap_send(&o, varbind);
203 
204 	*varcpy = v;
205 	*vcpylen = pdu->hdr->length;
206 
207 	return (AGENTX_ERR_NONE);
208  done:
209 	if (varbind != NULL)
210 		ber_free_elements(varbind);
211 	if (v)
212 		free(v);
213 	*idx = x;
214 	return (ret);
215 }
216 
217 int
218 trap_send(struct ber_oid *oid, struct ber_element *elm)
219 {
220 	int			 ret = 0, s;
221 	struct address		*tr;
222 	struct ber_element	*root, *b, *c, *trap;
223 	struct ber		 ber;
224 	char			*cmn;
225 	ssize_t			 len;
226 	u_int8_t		*ptr;
227 	struct			 ber_oid uptime = OID(MIB_sysUpTime);
228 	struct			 ber_oid trapoid = OID(MIB_snmpTrapOID);
229 	char			 ostr[SNMP_MAX_OID_STRLEN];
230 	struct oid		 oa, ob;
231 
232 	if (TAILQ_EMPTY(&env->sc_trapreceivers))
233 		return (0);
234 
235 	smi_scalar_oidlen(&uptime);
236 	smi_scalar_oidlen(&trapoid);
237 	smi_scalar_oidlen(oid);
238 
239 	smi_oid2string(oid, ostr, sizeof(ostr), 0);
240 	log_debug("trap_send: oid %s", ostr);
241 
242 	/* Setup OIDs to compare against the trap receiver MIB */
243 	bzero(&oa, sizeof(oa));
244 	bcopy(oid->bo_id, &oa.o_oid, sizeof(oa.o_oid));
245 	oa.o_oidlen = oid->bo_n;
246 	bzero(&ob, sizeof(ob));
247 	ob.o_flags = OID_TABLE;
248 
249 	/* Add mandatory varbind elements */
250 	trap = ber_add_sequence(NULL);
251 	c = ber_printf_elements(trap, "{Odt}{OO}",
252 	    &uptime, smi_getticks(),
253 	    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS,
254 	    &trapoid, oid);
255 	if (elm != NULL)
256 		ber_link_elements(c, elm);
257 
258 	bzero(&ber, sizeof(ber));
259 	ber.fd = -1;
260 
261 	TAILQ_FOREACH(tr, &env->sc_trapreceivers, entry) {
262 		if (tr->sa_oid != NULL && tr->sa_oid->bo_n) {
263 			/* The trap receiver may want only a specified MIB */
264 			bcopy(&tr->sa_oid->bo_id, &ob.o_oid,
265 			    sizeof(ob.o_oid));
266 			ob.o_oidlen = tr->sa_oid->bo_n;
267 			if (smi_oid_cmp(&oa, &ob) != 0)
268 				continue;
269 		}
270 
271 		if ((s = snmpd_socket_af(&tr->ss, htons(tr->port))) == -1) {
272 			ret = -1;
273 			goto done;
274 		}
275 
276 		cmn = tr->sa_community != NULL ?
277 		    tr->sa_community : env->sc_trcommunity;
278 
279 		/* SNMP header */
280 		root = ber_add_sequence(NULL);
281 		b = ber_printf_elements(root, "ds{tddd",
282 		    SNMP_V2, cmn, BER_CLASS_CONTEXT, SNMP_C_TRAPV2,
283 		    arc4random(), 0, 0);
284 		ber_link_elements(b, trap);
285 
286 #ifdef DEBUG
287 		smi_debug_elements(root);
288 #endif
289 		len = ber_write_elements(&ber, root);
290 		if (ber_get_writebuf(&ber, (void *)&ptr) > 0 &&
291 		    sendto(s, ptr, len, 0, (struct sockaddr *)&tr->ss,
292 		    tr->ss.ss_len) != -1) {
293 			env->sc_stats.snmp_outpkts++;
294 			ret++;
295 		}
296 
297 		close(s);
298 		ber_unlink_elements(b);
299 		ber_free_elements(root);
300 	}
301 
302  done:
303 	ber_free_elements(trap);
304 	ber_free(&ber);
305 
306 	return (ret);
307 }
308