1 /*
2 * ProFTPD - mod_snmp notification routines
3 * Copyright (c) 2008-2020 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 #include "mod_snmp.h"
26 #include "msg.h"
27 #include "pdu.h"
28 #include "smi.h"
29 #include "mib.h"
30 #include "packet.h"
31 #include "db.h"
32 #include "notify.h"
33
34 static const char *trace_channel = "snmp.notify";
35
36 struct snmp_notify_oid {
37 unsigned int notify_id;
38 oid_t notify_oid[SNMP_MIB_MAX_OIDLEN];
39 unsigned int notify_oidlen;
40 };
41
42 static struct snmp_notify_oid notify_oids[] = {
43 { SNMP_NOTIFY_DAEMON_MAX_INSTANCES,
44 { SNMP_MIB_DAEMON_NOTIFY_OID_MAX_INSTANCES, 0 },
45 SNMP_MIB_DAEMON_NOTIFY_OIDLEN_MAX_INSTANCES + 1 },
46
47 { SNMP_NOTIFY_FTP_BAD_PASSWD,
48 { SNMP_MIB_FTP_NOTIFY_OID_LOGIN_BAD_PASSWORD, 0 },
49 SNMP_MIB_FTP_NOTIFY_OIDLEN_LOGIN_BAD_PASSWORD + 1 },
50
51 { SNMP_NOTIFY_FTP_BAD_USER,
52 { SNMP_MIB_FTP_NOTIFY_OID_LOGIN_BAD_USER, 0 },
53 SNMP_MIB_FTP_NOTIFY_OIDLEN_LOGIN_BAD_USER + 1 },
54
55 { 0, { }, 0 }
56 };
57
get_notify_str(unsigned int notify_id)58 static const char *get_notify_str(unsigned int notify_id) {
59 const char *name = NULL;
60
61 switch (notify_id) {
62 case SNMP_NOTIFY_DAEMON_MAX_INSTANCES:
63 name = "maxInstancesExceeded";
64 break;
65
66 case SNMP_NOTIFY_FTP_BAD_PASSWD:
67 name = "loginFailedBadPassword";
68 break;
69
70 case SNMP_NOTIFY_FTP_BAD_USER:
71 name = "loginFailedBadUser";
72 break;
73
74 default:
75 name = "<Unknown>";
76 }
77
78 return name;
79 }
80
get_notify_oid(pool * p,unsigned int notify_id,unsigned int * oidlen)81 static oid_t *get_notify_oid(pool *p, unsigned int notify_id,
82 unsigned int *oidlen) {
83 register unsigned int i;
84
85 for (i = 0; notify_oids[i].notify_oidlen > 0; i++) {
86 if (notify_oids[i].notify_id == notify_id) {
87 *oidlen = notify_oids[i].notify_oidlen;
88 return notify_oids[i].notify_oid;
89 }
90 }
91
92 errno = ENOENT;
93 return NULL;
94 }
95
get_notify_pkt(pool * p,const char * community,const pr_netaddr_t * dst_addr,unsigned int notify_id,struct snmp_var ** head_var,struct snmp_var ** tail_var)96 static struct snmp_packet *get_notify_pkt(pool *p, const char *community,
97 const pr_netaddr_t *dst_addr, unsigned int notify_id,
98 struct snmp_var **head_var, struct snmp_var **tail_var) {
99 struct snmp_packet *pkt = NULL;
100 struct snmp_mib *mib = NULL;
101 struct snmp_var *resp_var = NULL;
102 int32_t mib_int = -1;
103 char *mib_str = NULL;
104 size_t mib_strlen = 0;
105 oid_t *notify_oid = NULL;
106 unsigned int notify_oidlen = 0;
107 int res;
108
109 pkt = snmp_packet_create(p);
110 pkt->snmp_version = SNMP_PROTOCOL_VERSION_2;
111 pkt->community = (char *) community;
112 pkt->community_len = strlen(community);
113 pkt->remote_addr = dst_addr;
114
115 pkt->resp_pdu = snmp_pdu_create(pkt->pool, SNMP_PDU_TRAP_V2);
116 pkt->resp_pdu->err_code = 0;
117 pkt->resp_pdu->err_idx = 0;
118 pkt->resp_pdu->request_id = snmp_notify_get_request_id();
119
120 /* Set first varbind to sysUptime.0 (1.3.6.1.2.1.1.3.0, TimeTicks),
121 * per RFC 1905, Section 4.2.6.
122 */
123 res = snmp_db_get_value(pkt->pool, SNMP_DB_NOTIFY_F_SYS_UPTIME, &mib_int,
124 &mib_str, &mib_strlen);
125 if (res < 0) {
126 int xerrno = errno;
127
128 (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
129 "unable to get system uptime for notification: %s", strerror(xerrno));
130
131 errno = xerrno;
132 return NULL;
133 }
134
135 mib = snmp_mib_get_by_idx(SNMP_MIB_SYS_UPTIME_IDX);
136 resp_var = snmp_smi_create_var(pkt->pool, mib->mib_oid, mib->mib_oidlen,
137 mib->smi_type, mib_int, mib_str, mib_strlen);
138 snmp_smi_util_add_list_var(head_var, tail_var, resp_var);
139
140 /* Set second varbind to snmpTrapOID.0 (1.3.6.1.6.3.1.1.4.1.0, OID)
141 * per RFC 1905, Section 4.2.6.
142 */
143 mib = snmp_mib_get_by_idx(SNMP_MIB_SNMP2_TRAP_OID_IDX);
144 notify_oid = get_notify_oid(pkt->pool, notify_id, ¬ify_oidlen);
145 resp_var = snmp_smi_create_oid(pkt->pool, mib->mib_oid, mib->mib_oidlen,
146 mib->smi_type, notify_oid, notify_oidlen);
147 snmp_smi_util_add_list_var(head_var, tail_var, resp_var);
148
149 return pkt;
150 }
151
get_notify_varlist(pool * p,unsigned int notify_id,struct snmp_var ** head_var)152 static int get_notify_varlist(pool *p, unsigned int notify_id,
153 struct snmp_var **head_var) {
154 struct snmp_var *tail_var = NULL;
155 int var_count = 0;
156
157 switch (notify_id) {
158 case SNMP_NOTIFY_DAEMON_MAX_INSTANCES: {
159 int32_t int_value = 0;
160 char *str_value = NULL;
161 size_t str_valuelen = 0;
162 int res;
163
164 /* The MaxInstances check happens very early on in the session
165 * lifecycle, thus there is not much information that we can add
166 * to notifications for this event, other than the fact that it
167 * happened.
168 *
169 * Per PROFTPD-MIB, we need to include:
170 *
171 * daemon.maxInstancesConfig
172 */
173
174 res = snmp_db_get_value(p, SNMP_DB_DAEMON_F_MAXINST_CONF, &int_value,
175 &str_value, &str_valuelen);
176 if (res < 0) {
177 pr_trace_msg(trace_channel, 5,
178 "unable to get daemon.maxInstancesConfig value: %s", strerror(errno));
179
180 } else {
181 oid_t oid[] = { SNMP_MIB_DAEMON_OID_MAXINST_CONF, 0 };
182 unsigned int oidlen = SNMP_MIB_DAEMON_OIDLEN_MAXINST_CONF + 1;
183 struct snmp_var *var;
184
185 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_INTEGER, int_value,
186 str_value, str_valuelen);
187 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
188 }
189
190 return var_count;
191 }
192
193 case SNMP_NOTIFY_FTP_BAD_PASSWD:
194 case SNMP_NOTIFY_FTP_BAD_USER: {
195 struct snmp_var *var;
196 int32_t int_value = 0;
197 char *str_value = NULL;
198 size_t str_valuelen = 0;
199 int res;
200
201 /* Per PROFTPD-MIB, we need to include:
202 *
203 * connection.serverName
204 * connection.serverAddress
205 * connection.serverPort
206 * connection.clientAddress
207 * connection.processId
208 * connection.userName
209 * connection.protocol
210 */
211
212 /* connection.serverName */
213 res = snmp_db_get_value(p, SNMP_DB_CONN_F_SERVER_NAME, &int_value,
214 &str_value, &str_valuelen);
215 if (res < 0) {
216 pr_trace_msg(trace_channel, 5,
217 "unable to get connection.serverName value: %s", strerror(errno));
218
219 } else {
220 oid_t oid[] = { SNMP_MIB_CONN_OID_SERVER_NAME, 0 };
221 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_SERVER_NAME + 1;
222
223 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_STRING, int_value,
224 str_value, str_valuelen);
225 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
226 }
227
228 /* connection.serverAddress */
229 res = snmp_db_get_value(p, SNMP_DB_CONN_F_SERVER_ADDR, &int_value,
230 &str_value, &str_valuelen);
231 if (res < 0) {
232 pr_trace_msg(trace_channel, 5,
233 "unable to get connection.serverAddress value: %s", strerror(errno));
234
235 } else {
236 oid_t oid[] = { SNMP_MIB_CONN_OID_SERVER_ADDR, 0 };
237 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_SERVER_ADDR + 1;
238
239 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_STRING, int_value,
240 str_value, str_valuelen);
241 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
242 }
243
244 /* connection.serverPort */
245 res = snmp_db_get_value(p, SNMP_DB_CONN_F_SERVER_PORT, &int_value,
246 &str_value, &str_valuelen);
247 if (res < 0) {
248 pr_trace_msg(trace_channel, 5,
249 "unable to get connection.serverPort value: %s", strerror(errno));
250
251 } else {
252 oid_t oid[] = { SNMP_MIB_CONN_OID_SERVER_PORT, 0 };
253 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_SERVER_PORT + 1;
254
255 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_INTEGER, int_value,
256 str_value, str_valuelen);
257 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
258 }
259
260 /* connection.clientAddress */
261 res = snmp_db_get_value(p, SNMP_DB_CONN_F_CLIENT_ADDR, &int_value,
262 &str_value, &str_valuelen);
263 if (res < 0) {
264 pr_trace_msg(trace_channel, 5,
265 "unable to get connection.clientAddress value: %s", strerror(errno));
266
267 } else {
268 oid_t oid[] = { SNMP_MIB_CONN_OID_CLIENT_ADDR, 0 };
269 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_CLIENT_ADDR + 1;
270
271 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_STRING, int_value,
272 str_value, str_valuelen);
273 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
274 }
275
276 /* connection.processId */
277 res = snmp_db_get_value(p, SNMP_DB_CONN_F_PID, &int_value, &str_value,
278 &str_valuelen);
279 if (res < 0) {
280 pr_trace_msg(trace_channel, 5,
281 "unable to get connection.processId value: %s", strerror(errno));
282
283 } else {
284 oid_t oid[] = { SNMP_MIB_CONN_OID_PID, 0 };
285 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_PID + 1;
286
287 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_INTEGER, int_value,
288 str_value, str_valuelen);
289 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
290 }
291
292 /* connection.userName */
293 res = snmp_db_get_value(p, SNMP_DB_CONN_F_USER_NAME, &int_value,
294 &str_value, &str_valuelen);
295 if (res < 0) {
296 pr_trace_msg(trace_channel, 5,
297 "unable to get connection.userName value: %s", strerror(errno));
298
299 } else {
300 oid_t oid[] = { SNMP_MIB_CONN_OID_USER_NAME, 0 };
301 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_USER_NAME + 1;
302
303 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_STRING, int_value,
304 str_value, str_valuelen);
305 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
306 }
307
308 /* connection.protocol */
309 res = snmp_db_get_value(p, SNMP_DB_CONN_F_PROTOCOL, &int_value,
310 &str_value, &str_valuelen);
311 if (res < 0) {
312 pr_trace_msg(trace_channel, 5,
313 "unable to get connection.protocol value: %s", strerror(errno));
314
315 } else {
316 oid_t oid[] = { SNMP_MIB_CONN_OID_PROTOCOL, 0 };
317 unsigned int oidlen = SNMP_MIB_CONN_OIDLEN_PROTOCOL + 1;
318
319 var = snmp_smi_create_var(p, oid, oidlen, SNMP_SMI_STRING, int_value,
320 str_value, str_valuelen);
321 var_count = snmp_smi_util_add_list_var(head_var, &tail_var, var);
322 }
323
324 return var_count;
325 }
326
327 default:
328 break;
329 }
330
331 errno = ENOENT;
332 return -1;
333 }
334
snmp_notify_generate(pool * p,int sockfd,const char * community,const pr_netaddr_t * src_addr,const pr_netaddr_t * dst_addr,unsigned int notify_id)335 int snmp_notify_generate(pool *p, int sockfd, const char *community,
336 const pr_netaddr_t *src_addr, const pr_netaddr_t *dst_addr,
337 unsigned int notify_id) {
338 const char *notify_str;
339 struct snmp_packet *pkt;
340 struct snmp_var *notify_varlist = NULL, *head_var = NULL, *tail_var = NULL,
341 *iter_var;
342 int fd = -1, res;
343 unsigned int var_count = 0;
344
345 notify_str = get_notify_str(notify_id);
346
347 pkt = get_notify_pkt(p, community, dst_addr, notify_id, &head_var, &tail_var);
348 if (pkt == NULL) {
349 int xerrno = errno;
350
351 pr_trace_msg(trace_channel, 7,
352 "unable to create %s notification packet: %s", notify_str,
353 strerror(xerrno));
354
355 errno = xerrno;
356 return -1;
357 }
358
359 /* Add trap-specific varbinds */
360 res = get_notify_varlist(p, notify_id, ¬ify_varlist);
361 if (res < 0) {
362 int xerrno = errno;
363
364 pr_trace_msg(trace_channel, 7,
365 "unable to create %s notification varbind list: %s", notify_str,
366 strerror(xerrno));
367
368 destroy_pool(pkt->pool);
369 errno = xerrno;
370 return -1;
371 }
372
373 for (iter_var = notify_varlist; iter_var; iter_var = iter_var->next) {
374 pr_signals_handle();
375
376 var_count = snmp_smi_util_add_list_var(&head_var, &tail_var, iter_var);
377 }
378
379 pkt->resp_pdu->varlist = head_var;
380 pkt->resp_pdu->varlistlen = var_count;
381
382 (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
383 "writing %s SNMP notification for %s, community = '%s', request ID %ld, "
384 "request type '%s'", notify_str,
385 snmp_msg_get_versionstr(pkt->snmp_version), pkt->community,
386 pkt->resp_pdu->request_id,
387 snmp_pdu_get_request_type_desc(pkt->resp_pdu->request_type));
388
389 res = snmp_msg_write(pkt->pool, &(pkt->resp_data), &(pkt->resp_datalen),
390 pkt->community, pkt->community_len, pkt->snmp_version, pkt->resp_pdu);
391 if (res < 0) {
392 int xerrno = errno;
393
394 (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
395 "error writing %s SNMP notification to UDP packet: %s", notify_str,
396 strerror(xerrno));
397
398 destroy_pool(pkt->pool);
399 errno = xerrno;
400 return -1;
401 }
402
403 if (sockfd < 0) {
404 /* If the given fd isn't open, then we need to open our own. */
405
406 /* XXX Support IPv6? */
407
408 fd = socket(AF_INET, SOCK_DGRAM, snmp_proto_udp);
409 if (fd < 0) {
410 int xerrno = errno;
411
412 (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
413 "unable to create UDP socket: %s", strerror(xerrno));
414
415 destroy_pool(pkt->pool);
416 errno = xerrno;
417 return -1;
418 }
419
420 } else {
421 fd = sockfd;
422 }
423
424 snmp_packet_write(p, fd, pkt);
425
426 /* If we opened our own socket here, then close it. */
427 if (sockfd < 0) {
428 (void) close(fd);
429 }
430
431 res = snmp_db_incr_value(pkt->pool, SNMP_DB_SNMP_F_TRAPS_SENT_TOTAL, 1);
432 if (res < 0) {
433 (void) pr_log_writefile(snmp_logfd, MOD_SNMP_VERSION,
434 "error incrementing snmp.trapsSentTotal: %s", strerror(errno));
435 }
436
437 destroy_pool(pkt->pool);
438 return 0;
439 }
440
snmp_notify_get_request_id(void)441 long snmp_notify_get_request_id(void) {
442 return pr_random_next(1L, 10000L);
443 }
444
snmp_notify_poll_cond(void)445 void snmp_notify_poll_cond(void) {
446 /* XXX Poll for notify conditions here, based on the criteria configured
447 * for various notification receivers.
448 */
449 }
450