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