1 /*
2  * ProFTPD: mod_unique_id -- a module for generating a unique ID for each
3  *                           FTP session.
4  * Copyright (c) 2006-2017 TJ Saunders
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19  *
20  * As a special exemption, TJ Saunders and other respective copyright holders
21  * give permission to link this program with OpenSSL, and distribute the
22  * resulting executable, without including the source code for OpenSSL in the
23  * source distribution.
24  *
25  * This is mod_unique_id, contrib software for proftpd 1.2.x/1.3.x and above.
26  * For more information contact TJ Saunders <tj@castaglia.org>.
27  */
28 
29 #include "conf.h"
30 
31 /* A lot of ideas for this module were liberally borrowed from the mod_uniq_id
32  * module for Apache.
33  */
34 
35 #define MOD_UNIQUE_ID_VERSION		"mod_unique_id/0.2"
36 
37 /* Make sure the version of proftpd is as necessary. */
38 #if PROFTPD_VERSION_NUMBER < 0x0001030402
39 # error "ProFTPD 1.3.4rc2 or later required"
40 #endif
41 
42 module unique_id_module;
43 
44 static const char base64[64] = {
45   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
46   'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
47   'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
48   't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
49   '8', '9', '+', '/'
50 };
51 
52 static unsigned int host_ipaddr = 0;
53 
54 /* Configuration handlers
55  */
56 
57 /* usage: UniqueIDEngine on|off */
set_uniqueidengine(cmd_rec * cmd)58 MODRET set_uniqueidengine(cmd_rec *cmd) {
59   int bool = -1;
60   config_rec *c = NULL;
61 
62   CHECK_ARGS(cmd, 1);
63   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
64 
65   bool = get_boolean(cmd, 1);
66   if (bool == -1)
67     CONF_ERROR(cmd, "expected Boolean parameter");
68 
69   c = add_config_param(cmd->argv[0], 1, NULL);
70   c->argv[0] = pcalloc(c->pool, sizeof(int));
71   *((int *) c->argv[0]) = bool;
72 
73   return PR_HANDLED(cmd);
74 }
75 
76 /* Event handlers
77  */
78 
79 #if defined(PR_SHARED_MODULE)
uniqid_mod_unload_ev(const void * event_data,void * user_data)80 static void uniqid_mod_unload_ev(const void *event_data, void *user_data) {
81   if (strcmp("mod_unique_id.c", (const char *) event_data) == 0) {
82     /* Unregister ourselves from all events. */
83     pr_event_unregister(&unique_id_module, NULL, NULL);
84   }
85 }
86 #endif /* PR_SHARED_MODULE */
87 
uniqid_postparse_ev(const void * event_data,void * user_data)88 static void uniqid_postparse_ev(const void *event_data, void *user_data) {
89   pool *tmp_pool = make_sub_pool(main_server->pool);
90   const char *host_name = NULL;
91   const pr_netaddr_t *host_addr = NULL;
92   void *addr_data = NULL;
93 
94   host_name = pr_netaddr_get_localaddr_str(tmp_pool);
95   if (host_name == NULL) {
96     pr_log_pri(PR_LOG_WARNING, MOD_UNIQUE_ID_VERSION
97       ": unable to determine hostname");
98     destroy_pool(tmp_pool);
99     pr_session_disconnect(&unique_id_module, PR_SESS_DISCONNECT_BY_APPLICATION,
100       NULL);
101   }
102 
103   host_addr = pr_netaddr_get_addr(tmp_pool, host_name, NULL);
104   if (host_addr == NULL) {
105     pr_log_pri(PR_LOG_WARNING, MOD_UNIQUE_ID_VERSION
106       ": unable to resolve '%s' to an IP address", host_name);
107     destroy_pool(tmp_pool);
108     pr_session_disconnect(&unique_id_module, PR_SESS_DISCONNECT_BY_APPLICATION,
109       NULL);
110   }
111 
112   addr_data = pr_netaddr_get_inaddr(host_addr);
113   if (addr_data) {
114     /* Copy bits from addr_data up to the size of an unsigned int (32 bits,
115      * ideally).  In the case of an IPv6 address, this will only use the
116      * low-order bits of the entire address.  C'est la vie.
117      */
118     memcpy(&host_ipaddr, addr_data, sizeof(host_ipaddr));
119   }
120 
121   destroy_pool(tmp_pool);
122   return;
123 }
124 
125 /* Initialization functions
126  */
127 
uniqid_init(void)128 static int uniqid_init(void) {
129 
130 #if defined(PR_SHARED_MODULE)
131   pr_event_register(&unique_id_module, "core.module-unload",
132     uniqid_mod_unload_ev, NULL);
133 #endif /* PR_SHARED_MODULE */
134   pr_event_register(&unique_id_module, "core.postparse", uniqid_postparse_ev,
135     NULL);
136 
137   return 0;
138 }
139 
uniqid_sess_init(void)140 static int uniqid_sess_init(void) {
141   config_rec *c;
142   int uniqid_engine = TRUE;
143   unsigned int client_ipaddr = 0;
144   void *addr_data = NULL;
145   unsigned int pid;
146   unsigned int now;
147   unsigned short counter;
148   struct timeval tv;
149   struct timezone tz;
150   char *key = "UNIQUE_ID", *id = NULL;
151   unsigned char *x, *y;
152   register unsigned int i, j;
153 
154   /* Four pieces of data as unsigned ints:
155    *
156    *  host IP address
157    *  client IP address
158    *  PID
159    *  timestamp (in seconds)
160    *
161    * and one piece of unsigned short data:
162    *
163    *  counter (derived from number of microsecond)
164    */
165   unsigned short id_sz = (sizeof(unsigned int) * 4) + (sizeof(unsigned short));
166   unsigned short id_encoded_sz = (id_sz * 8 + 5) / 6;
167   unsigned char buf[id_sz];
168 
169   c = find_config(main_server->conf, CONF_PARAM, "UniqueIDEngine", FALSE);
170   if (c) {
171     uniqid_engine = *((int *) c->argv[0]);
172   }
173 
174   if (!uniqid_engine)
175     return 0;
176 
177   pr_log_debug(DEBUG8, MOD_UNIQUE_ID_VERSION ": generating unique session ID");
178 
179   if (gettimeofday(&tv, &tz) < 0) {
180     pr_log_debug(DEBUG1, MOD_UNIQUE_ID_VERSION
181       ": error getting time of day: %s", strerror(errno));
182     now = 0;
183     counter = 0;
184 
185   } else {
186     now = htonl((unsigned int) tv.tv_sec);
187     counter = htons((unsigned short) (tv.tv_usec / 10));
188   }
189 
190   pid = htonl((unsigned int) getpid());
191 
192   addr_data = pr_netaddr_get_inaddr(session.c->remote_addr);
193   if (addr_data) {
194     /* Copy bits from addr_data up to the size of an unsigned int (32 bits,
195      * ideally).  In the case of an IPv6 address, this will only use the
196      * low-order bits of the entire address.  C'est la vie.
197      */
198     memcpy(&client_ipaddr, addr_data, sizeof(client_ipaddr));
199   }
200 
201   /* Populate buf with the binary pieces of data we've collected. */
202   memset(buf, '\0', sizeof(buf));
203   x = buf;
204 
205   j = 0;
206   y = (unsigned char *) &now;
207   for (i = 0; i < sizeof(unsigned int); i++, j++) {
208     x[j] = y[i];
209   }
210 
211   y = (unsigned char *) &host_ipaddr;
212   for (i = 0; i < sizeof(unsigned int); i++, j++) {
213     x[j] = y[i];
214   }
215 
216   y = (unsigned char *) &client_ipaddr;
217   for (i = 0; i < sizeof(unsigned int); i++, j++) {
218     x[j] = y[i];
219   }
220 
221   y = (unsigned char *) &pid;
222   for (i = 0; i < sizeof(unsigned int); i++, j++) {
223     x[j] = y[i];
224   }
225 
226   y = (unsigned char *) &counter;
227   for (i = 0; i < sizeof(unsigned short); i++, j++) {
228     x[j] = y[i];
229   }
230 
231   /* Add one to the encoded size, for the trailing NUL. */
232   id = pcalloc(session.pool, id_encoded_sz + 1);
233 
234   j = 0;
235   for (i = 0; i < id_sz; i += 3) {
236     y = x + i;
237 
238     id[j++] = base64[y[0] >> 2];
239     id[j++] = base64[((y[0] & 0x03) << 4) | ((y[1] & 0xf0) >> 4)];
240 
241     if (j == id_encoded_sz)
242       break;
243 
244     id[j++] = base64[((y[1] & 0x0f) << 2) | ((y[2] & 0xc0) >> 6)];
245 
246     if (j == id_encoded_sz)
247       break;
248 
249     id[j++] = base64[y[2] & 0x3f];
250   }
251 
252   if (j >= id_encoded_sz)
253     j = id_encoded_sz;
254   id[j] = '\0';
255 
256   if (pr_env_set(session.pool, key, id) < 0) {
257     pr_log_debug(DEBUG0, MOD_UNIQUE_ID_VERSION
258       ": error setting UNIQUE_ID environment variable: %s", strerror(errno));
259 
260   } else {
261     pr_log_debug(DEBUG8, MOD_UNIQUE_ID_VERSION
262       ": unique session ID is '%s'", id);
263   }
264 
265   if (pr_table_add_dup(session.notes, pstrdup(session.pool, key), id, 0) < 0) {
266     pr_log_debug(DEBUG0, MOD_UNIQUE_ID_VERSION
267       ": error adding %s session note: %s", key, strerror(errno));
268   }
269 
270   return 0;
271 }
272 
273 /* Module API tables
274  */
275 
276 static conftable uniqid_conftab[] = {
277   { "UniqueIDEngine",	set_uniqueidengine,	NULL },
278   { NULL }
279 };
280 
281 module unique_id_module = {
282   NULL, NULL,
283 
284   /* Module API version 2.0 */
285   0x20,
286 
287   /* Module name */
288   "unique_id",
289 
290   /* Module configuration handler table */
291   uniqid_conftab,
292 
293   /* Module command handler table */
294   NULL,
295 
296   /* Module authentication handler table */
297   NULL,
298 
299   /* Module initialization function */
300   uniqid_init,
301 
302   /* Session initialization function */
303   uniqid_sess_init,
304 
305   /* Module version */
306   MOD_UNIQUE_ID_VERSION
307 };
308