xref: /openbsd-src/usr.sbin/snmpd/trap.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: trap.c,v 1.34 2019/12/09 16:51:10 martijn 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/types.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/tree.h>
25 
26 #include <net/if.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <pwd.h>
38 
39 #include "snmpd.h"
40 #include "mib.h"
41 
42 void
43 trap_init(void)
44 {
45 	struct ber_oid	 trapoid = OID(MIB_coldStart);
46 
47 	/*
48 	 * Send a coldStart to notify that the daemon has been
49 	 * started and re-initialized.
50 	 */
51 	trap_send(&trapoid, NULL);
52 }
53 
54 int
55 trap_agentx(struct agentx_handle *h, struct agentx_pdu *pdu, int *idx,
56     char **varcpy, int *vcpylen)
57 {
58 	struct agentx_varbind_hdr	 vbhdr;
59 	u_int32_t			 d;
60 	struct ber_oid			 o, oid;
61 	struct ber_oid			 uptime = OID(MIB_sysUpTime);
62 	struct ber_oid			 trapoid = OID(MIB_snmpTrapOID);
63 	struct ber_element		*varbind, *iter;
64 	int				 x = 0, state = 0;
65 	int				 ret = AGENTX_ERR_NONE;
66 	int				 seensysuptime, seentrapoid;
67 	size_t				 len = 0;
68 	char				*v = NULL;
69 
70 	*varcpy = NULL;
71 	varbind = NULL;
72 	iter = NULL;
73 	seensysuptime = seentrapoid = 0;
74 
75 	if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) {
76 		ret = AGENTX_ERR_UNSUPPORTED_CONTEXT;
77 		goto done;
78 	}
79 
80 	if ((v = malloc(pdu->hdr->length)) == NULL ||
81 	    snmp_agentx_copy_raw(pdu, v, pdu->hdr->length) == -1) {
82 		ret = AGENTX_ERR_PROCESSING_ERROR;
83 		goto done;
84 	}
85 
86 	smi_scalar_oidlen(&uptime);
87 	smi_scalar_oidlen(&trapoid);
88 	while (pdu->datalen > sizeof(struct agentx_hdr)) {
89 		x++;
90 
91 		if (snmp_agentx_read_vbhdr(pdu, &vbhdr) == -1) {
92 			ret = AGENTX_ERR_PARSE_ERROR;
93 			goto done;
94 		}
95 
96 		if (state < 2) {
97 			if (snmp_agentx_read_oid(pdu, (struct snmp_oid *)&oid) == -1) {
98 				ret = AGENTX_ERR_PARSE_ERROR;
99 				goto done;
100 			}
101 			if (state == 0 && ober_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 (ober_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 		ret = varbind_convert(pdu, &vbhdr, &varbind, &iter);
123 		if (ret != AGENTX_ERR_NONE)
124 			goto done;
125 	}
126 
127 	if (varbind != NULL)
128 		len = ober_calc_len(varbind);
129 	log_debug("trap_agentx: from packetid %d len %zu elements %d",
130 	    pdu->hdr->packetid, len, x);
131 
132 	trap_send(&o, varbind);
133 
134 	*varcpy = v;
135 	*vcpylen = pdu->hdr->length;
136 
137 	return (AGENTX_ERR_NONE);
138  done:
139 	if (varbind != NULL)
140 		ober_free_elements(varbind);
141 	free(v);
142 	*idx = x;
143 	return (ret);
144 }
145 
146 int
147 trap_send(struct ber_oid *oid, struct ber_element *elm)
148 {
149 	int			 ret = 0, s;
150 	struct address		*tr;
151 	struct ber_element	*root, *b, *c, *trap;
152 	struct ber		 ber;
153 	char			*cmn;
154 	ssize_t			 len;
155 	u_int8_t		*ptr;
156 	struct			 ber_oid uptime = OID(MIB_sysUpTime);
157 	struct			 ber_oid trapoid = OID(MIB_snmpTrapOID);
158 	char			 ostr[SNMP_MAX_OID_STRLEN];
159 	struct oid		 oa, ob;
160 
161 	if (TAILQ_EMPTY(&snmpd_env->sc_trapreceivers))
162 		return (0);
163 
164 	smi_scalar_oidlen(&uptime);
165 	smi_scalar_oidlen(&trapoid);
166 	smi_scalar_oidlen(oid);
167 
168 	smi_oid2string(oid, ostr, sizeof(ostr), 0);
169 	log_debug("trap_send: oid %s", ostr);
170 
171 	/* Setup OIDs to compare against the trap receiver MIB */
172 	bzero(&oa, sizeof(oa));
173 	bcopy(oid->bo_id, &oa.o_oid, sizeof(oa.o_oid));
174 	oa.o_oidlen = oid->bo_n;
175 	bzero(&ob, sizeof(ob));
176 	ob.o_flags = OID_TABLE;
177 
178 	/* Add mandatory varbind elements */
179 	trap = ober_add_sequence(NULL);
180 	c = ober_printf_elements(trap, "{Odt}{OO}",
181 	    &uptime, smi_getticks(),
182 	    BER_CLASS_APPLICATION, SNMP_T_TIMETICKS,
183 	    &trapoid, oid);
184 	if (elm != NULL)
185 		ober_link_elements(c, elm);
186 
187 	bzero(&ber, sizeof(ber));
188 
189 	TAILQ_FOREACH(tr, &snmpd_env->sc_trapreceivers, entry) {
190 		if (tr->sa_oid != NULL && tr->sa_oid->bo_n) {
191 			/* The trap receiver may want only a specified MIB */
192 			bcopy(&tr->sa_oid->bo_id, &ob.o_oid,
193 			    sizeof(ob.o_oid));
194 			ob.o_oidlen = tr->sa_oid->bo_n;
195 			if (smi_oid_cmp(&oa, &ob) != 0)
196 				continue;
197 		}
198 
199 		if ((s = snmpd_socket_af(&tr->ss, htons(tr->port),
200 		    IPPROTO_UDP)) == -1) {
201 			ret = -1;
202 			goto done;
203 		}
204 		if (tr->sa_srcaddr != NULL) {
205 			if (bind(s, (struct sockaddr *)&tr->sa_srcaddr->ss,
206 			    tr->sa_srcaddr->ss.ss_len) == -1) {
207 				ret = -1;
208 				goto done;
209 			}
210 		}
211 
212 		cmn = tr->sa_community != NULL ?
213 		    tr->sa_community : snmpd_env->sc_trcommunity;
214 
215 		/* SNMP header */
216 		root = ober_add_sequence(NULL);
217 		b = ober_printf_elements(root, "ds{tddd",
218 		    SNMP_V2, cmn, BER_CLASS_CONTEXT, SNMP_C_TRAPV2,
219 		    arc4random(), 0, 0);
220 		ober_link_elements(b, trap);
221 
222 #ifdef DEBUG
223 		smi_debug_elements(root);
224 #endif
225 		len = ober_write_elements(&ber, root);
226 		if (ober_get_writebuf(&ber, (void *)&ptr) > 0 &&
227 		    sendto(s, ptr, len, 0, (struct sockaddr *)&tr->ss,
228 		    tr->ss.ss_len) != -1) {
229 			snmpd_env->sc_stats.snmp_outpkts++;
230 			ret++;
231 		}
232 
233 		close(s);
234 		ober_unlink_elements(b);
235 		ober_free_elements(root);
236 	}
237 
238  done:
239 	ober_free_elements(trap);
240 	ober_free(&ber);
241 
242 	return (ret);
243 }
244