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, &notify_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, &notify_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