1 /*
2  * ProFTPD: mod_radius -- a module for RADIUS authentication and accounting
3  * Copyright (c) 2001-2021 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 gives permission to link this program
20  * with OpenSSL, and distribute the resulting executable, without including
21  * the source code for OpenSSL in the source distribution.
22  *
23  * This is mod_radius, contrib software for proftpd 1.2 and above.
24  * For more information contact TJ Saunders <tj@castaglia.org>.
25  *
26  * This module is based in part on code in Alan DeKok's (aland@freeradius.org)
27  * mod_auth_radius for Apache, in part on the FreeRADIUS project's code.
28  */
29 
30 #define MOD_RADIUS_VERSION	"mod_radius/0.9.3"
31 
32 #include "conf.h"
33 #include "privs.h"
34 
35 /* RADIUS information */
36 
37 /* From RFC2865, RFC2866 */
38 #define RADIUS_AUTH_PORT	1812
39 #define RADIUS_ACCT_PORT	1813
40 
41 #define RADIUS_PASSWD_LEN	16
42 #define RADIUS_VECTOR_LEN	16
43 
44 /* From RFC2138 */
45 #define RADIUS_STRING_LEN	254
46 
47 /* RADIUS attribute structures */
48 typedef struct {
49   unsigned char type;
50   unsigned char length;
51   unsigned char data[1];
52 } radius_attrib_t;
53 
54 /* RADIUS packet header */
55 typedef struct {
56   unsigned char code;
57   unsigned char id;
58   unsigned short length;
59   unsigned char digest[RADIUS_VECTOR_LEN];
60   unsigned char data[2];
61 
62   char _pad[PR_TUNABLE_BUFFER_SIZE];
63 } radius_packet_t;
64 
65 #define RADIUS_HEADER_LEN	20
66 
67 /* RADIUS ID Definitions (see RFC 2865, 2866) */
68 #define RADIUS_AUTH_REQUEST		1
69 #define RADIUS_AUTH_ACCEPT		2
70 #define RADIUS_AUTH_REJECT		3
71 #define RADIUS_ACCT_REQUEST		4
72 #define RADIUS_ACCT_RESPONSE		5
73 #define RADIUS_ACCT_STATUS		6
74 #define RADIUS_AUTH_CHALLENGE		11
75 
76 /* RADIUS Attribute Definitions (see RFC 2865, 2866) */
77 #define RADIUS_USER_NAME		1
78 #define RADIUS_PASSWORD			2
79 #define RADIUS_NAS_IP_ADDRESS		4
80 #define RADIUS_NAS_PORT			5
81 #define RADIUS_SERVICE_TYPE		6
82 #define RADIUS_OLD_PASSWORD		17
83 #define RADIUS_REPLY_MESSAGE		18
84 #define RADIUS_STATE			24
85 #define RADIUS_CLASS			25
86 #define RADIUS_VENDOR_SPECIFIC		26
87 #define RADIUS_SESSION_TIMEOUT		27
88 #define RADIUS_IDLE_TIMEOUT		28
89 #define RADIUS_CALLING_STATION_ID	31
90 #define RADIUS_NAS_IDENTIFIER		32
91 #define RADIUS_ACCT_STATUS_TYPE		40
92 #define RADIUS_ACCT_INPUT_OCTETS	42
93 #define RADIUS_ACCT_OUTPUT_OCTETS	43
94 #define RADIUS_ACCT_SESSION_ID		44
95 #define RADIUS_ACCT_AUTHENTIC		45
96 #define RADIUS_ACCT_SESSION_TIME	46
97 #define RADIUS_ACCT_TERMINATE_CAUSE	49
98 #define RADIUS_ACCT_EVENT_TS		55
99 #define RADIUS_NAS_PORT_TYPE		61
100 #define RADIUS_MESSAGE_AUTHENTICATOR	80
101 #define RADIUS_NAS_IPV6_ADDRESS		95
102 
103 /* RADIUS service types */
104 #define RADIUS_SVC_LOGIN		1
105 #define RADIUS_SVC_AUTHENTICATE_ONLY	8
106 
107 /* RADIUS status types */
108 #define RADIUS_ACCT_STATUS_START	1
109 #define RADIUS_ACCT_STATUS_STOP		2
110 #define RADIUS_ACCT_STATUS_ALIVE	3
111 
112 /* RADIUS NAS port types */
113 #define RADIUS_NAS_PORT_TYPE_VIRTUAL	5
114 
115 /* RADIUS authentication types */
116 #define RADIUS_AUTH_NONE		0
117 #define RADIUS_AUTH_RADIUS		1
118 #define RADIUS_AUTH_LOCAL		2
119 
120 /* RADIUS Acct-Terminate-Cause types */
121 #define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST	1
122 #define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE	3
123 #define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT	4
124 #define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT	5
125 #define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET		6
126 #define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT	7
127 #define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAIL	15
128 #define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR		16
129 
130 /* The RFC says 4096 octets max, and most packets are less than 256.
131  * However, this number is just larger than the maximum MTU of just
132  * most types of networks, except maybe for gigabit ethernet.
133  */
134 #define RADIUS_PACKET_LEN		1600
135 
136 /* Miscellaneous default values */
137 #define DEFAULT_RADIUS_TIMEOUT		10
138 
139 #define RADIUS_ATTRIB_LEN(attr)		((attr)->length)
140 
141 /* Adjust the VSA length (I'm not sure why this is necessary, but a reading
142  * of the FreeRADIUS sources show it to be.  Weird.)
143  */
144 #define RADIUS_VSA_ATTRIB_LEN(attr)	((attr)->length - 2)
145 
146 typedef struct radius_server_obj {
147 
148   /* Next server in line */
149   struct radius_server_obj *next;
150 
151   /* Memory pool for this object */
152   pool *pool;
153 
154   /* RADIUS server IP address */
155   const pr_netaddr_t *addr;
156 
157   /* RADIUS server port */
158   unsigned short port;
159 
160   /* RADIUS server shared secret */
161   unsigned char *secret;
162   size_t secret_len;
163 
164   /* How long to wait for RADIUS responses */
165   unsigned int timeout;
166 
167 } radius_server_t;
168 
169 module radius_module;
170 
171 static pool *radius_pool = NULL;
172 static int radius_engine = FALSE;
173 static radius_server_t *radius_acct_server = NULL;
174 static radius_server_t *radius_auth_server = NULL;
175 static int radius_logfd = -1;
176 
177 /* mod_radius option flags */
178 #define RADIUS_OPT_IGNORE_REPLY_MESSAGE_ATTR		0x0001
179 #define RADIUS_OPT_IGNORE_CLASS_ATTR			0x0002
180 #define RADIUS_OPT_IGNORE_SESSION_TIMEOUT_ATTR		0x0004
181 #define RADIUS_OPT_IGNORE_IDLE_TIMEOUT_ATTR		0x0008
182 #define RADIUS_OPT_REQUIRE_MAC				0x0010
183 
184 static unsigned long radius_opts = 0UL;
185 
186 static struct sockaddr radius_local_sock, radius_remote_sock;
187 
188 /* For tracking various values not stored in the session struct */
189 static const char *radius_nas_identifier_config = NULL;
190 static char *radius_realm = NULL;
191 static time_t radius_session_start = 0;
192 static int radius_session_authtype = RADIUS_AUTH_LOCAL;
193 static unsigned char radius_auth_ok = FALSE;
194 static unsigned char radius_auth_reject = FALSE;
195 
196 /* For tracking the Class attribute, for sending in accounting requests. */
197 static char *radius_acct_class = NULL;
198 static size_t radius_acct_classlen = 0;
199 static char *radius_acct_user = NULL;
200 static size_t radius_acct_userlen = 0;
201 
202 /* "Fake" user/group information for RADIUS users. */
203 static unsigned char radius_have_user_info = FALSE;
204 static struct passwd radius_passwd;
205 
206 static unsigned char radius_have_group_info = FALSE;
207 static char *radius_prime_group_name = NULL;
208 static unsigned int radius_addl_group_count = 0;
209 static char **radius_addl_group_names = NULL;
210 static char *radius_addl_group_names_str = NULL;
211 static gid_t *radius_addl_group_ids = NULL;
212 static char *radius_addl_group_ids_str = NULL;
213 
214 /* Quota info */
215 static unsigned char radius_have_quota_info = FALSE;
216 static char *radius_quota_per_sess = NULL;
217 static char *radius_quota_limit_type = NULL;
218 static char *radius_quota_bytes_in = NULL;
219 static char *radius_quota_bytes_out = NULL;
220 static char *radius_quota_bytes_xfer = NULL;
221 static char *radius_quota_files_in = NULL;
222 static char *radius_quota_files_out = NULL;
223 static char *radius_quota_files_xfer = NULL;
224 
225 /* Other info */
226 static unsigned char radius_have_other_info = FALSE;
227 
228 /* Vendor information, defaults to Unix (Vendor-Id of 4) */
229 static const char *radius_vendor_name = "Unix";
230 static unsigned int radius_vendor_id = 4;
231 
232 /* Custom VSA IDs that may be used for server-supplied RadiusUserInfo
233  * parameters.
234  */
235 static int radius_uid_attr_id = 0;
236 static int radius_gid_attr_id = 0;
237 static int radius_home_attr_id = 0;
238 static int radius_shell_attr_id = 0;
239 
240 /* Custom VSA IDs that may be used for server-supplied RadiusGroupInfo
241  * parameters.
242  */
243 static int radius_prime_group_name_attr_id = 0;
244 static int radius_addl_group_names_attr_id = 0;
245 static int radius_addl_group_ids_attr_id = 0;
246 
247 /* Custom VSA IDs that may be used for server-supplied QuotaLimitTable
248  * parameters.
249  */
250 static int radius_quota_per_sess_attr_id = 0;
251 static int radius_quota_limit_type_attr_id = 0;
252 static int radius_quota_bytes_in_attr_id = 0;
253 static int radius_quota_bytes_out_attr_id = 0;
254 static int radius_quota_bytes_xfer_attr_id = 0;
255 static int radius_quota_files_in_attr_id = 0;
256 static int radius_quota_files_out_attr_id = 0;
257 static int radius_quota_files_xfer_attr_id = 0;
258 
259 /* For tracking the ID of the last accounting packet (to prevent the
260  * same ID from being reused).
261  */
262 static unsigned char radius_last_acct_pkt_id = 0;
263 
264 static const char *trace_channel = "radius";
265 
266 /* Convenience macros. */
267 #define RADIUS_IS_VAR(str) \
268   ((str[0] == '$') && (str[1] == '(') && (str[strlen(str)-1] == ')'))
269 
270 /* Function prototypes. */
271 static radius_attrib_t *radius_add_attrib(radius_packet_t *, unsigned char,
272   const unsigned char *, size_t);
273 static void radius_add_passwd(radius_packet_t *, unsigned char,
274   const unsigned char *, unsigned char *, size_t);
275 static void radius_build_packet(radius_packet_t *, const unsigned char *,
276   const unsigned char *, unsigned char *, size_t);
277 static unsigned char radius_have_var(char *);
278 static radius_attrib_t *radius_get_attrib(radius_packet_t *, unsigned char);
279 static radius_attrib_t *radius_get_next_attrib(radius_packet_t *,
280   unsigned char, unsigned int *, radius_attrib_t *);
281 static void radius_get_rnd_digest(radius_packet_t *);
282 static radius_attrib_t *radius_get_vendor_attrib(radius_packet_t *,
283   unsigned char);
284 static void radius_set_acct_digest(radius_packet_t *, const unsigned char *,
285   size_t);
286 static void radius_set_auth_mac(radius_packet_t *, const unsigned char *,
287   size_t);
288 static radius_server_t *radius_make_server(pool *);
289 static int radius_openlog(void);
290 static int radius_open_socket(void);
291 static unsigned char radius_parse_gids_str(pool *, char *, gid_t **,
292   unsigned int *);
293 static unsigned char radius_parse_groups_str(pool *, char *, char ***,
294   unsigned int *);
295 static int radius_parse_var(char *, int *, char **);
296 static int radius_process_accept_packet(radius_packet_t *,
297   const unsigned char *, size_t);
298 static int radius_process_reject_packet(radius_packet_t *,
299   const unsigned char *, size_t);
300 static void radius_process_group_info(config_rec *);
301 static void radius_process_quota_info(config_rec *);
302 static void radius_process_user_info(config_rec *);
303 static radius_packet_t *radius_recv_packet(int, unsigned int);
304 static int radius_send_packet(int, radius_packet_t *, radius_server_t *);
305 static int radius_start_accting(void);
306 static int radius_stop_accting(void);
307 static int radius_verify_auth_mac(radius_packet_t *, const char *,
308   const unsigned char *, size_t);
309 static int radius_verify_packet(radius_packet_t *, radius_packet_t *,
310   const unsigned char *, size_t);
311 static int radius_sess_init(void);
312 
313 /* Support functions
314  */
315 
radius_argsep(char ** arg)316 static char *radius_argsep(char **arg) {
317   char *ret = NULL, *dst = NULL;
318   char quote_mode = 0;
319 
320   if (!arg || !*arg || !**arg)
321     return NULL;
322 
323   while (**arg && PR_ISSPACE(**arg)) {
324     (*arg)++;
325   }
326 
327   if (!**arg)
328     return NULL;
329 
330   ret = dst = *arg;
331 
332   if (**arg == '\"') {
333     quote_mode++;
334     (*arg)++;
335   }
336 
337   while (**arg && **arg != ',' &&
338       (quote_mode ? (**arg != '\"') : (!PR_ISSPACE(**arg)))) {
339 
340     if (**arg == '\\' && quote_mode) {
341 
342       /* escaped char */
343       if (*((*arg) + 1))
344         *dst = *(++(*arg));
345     }
346 
347     *dst++ = **arg;
348     ++(*arg);
349   }
350 
351   if (**arg)
352     (*arg)++;
353 
354   *dst = '\0';
355   return ret;
356 }
357 
358 /* Check a "$(attribute-id:default)" string for validity. */
radius_have_var(char * var)359 static unsigned char radius_have_var(char *var) {
360   int id = 0;
361   char *ptr = NULL;
362   size_t varlen;
363 
364   varlen = strlen(var);
365 
366   /* Must be at least six characters. */
367   if (varlen < 7) {
368     return FALSE;
369   }
370 
371   /* Must start with '$(', and end with ')'. */
372   if (RADIUS_IS_VAR(var) == FALSE) {
373     return FALSE;
374   }
375 
376   /* Must have a ':'. */
377   ptr = strchr(var, ':');
378   if (ptr == NULL) {
379     return FALSE;
380   }
381 
382   /* ':' must be between '(' and ')'. */
383   if (ptr < (var + 3) ||
384       ptr > &var[varlen-2]) {
385     return FALSE;
386   }
387 
388   /* Parse out the component int/string. */
389   radius_parse_var(var, &id, NULL);
390 
391   /* Int must be greater than zero. */
392   if (id < 1) {
393     return FALSE;
394   }
395 
396   return TRUE;
397 }
398 
399 /* Separate the given "$(attribute-id:default)" string into its constituent
400  * custom attribute ID (int) and default (string) components.
401  */
radius_parse_var(char * var,int * attr_id,char ** attr_default)402 static int radius_parse_var(char *var, int *attr_id, char **attr_default) {
403   pool *tmp_pool;
404   char *var_cpy, *ptr = NULL;
405   size_t var_len, var_cpylen;
406 
407   if (var == NULL) {
408     errno = EINVAL;
409     return -1;
410   }
411 
412   var_len = var_cpylen = strlen(var);
413   if (var_len == 0) {
414     /* Empty string; nothing to do. */
415     return 0;
416   }
417 
418   tmp_pool = make_sub_pool(radius_pool);
419   var_cpy = pstrdup(tmp_pool, var);
420 
421   /* First, strip off the "$()" variable characters. */
422   var_cpy[var_cpylen-1] = '\0';
423   var_cpy += 2;
424 
425   /* Find the delimiting ':' */
426   ptr = strchr(var_cpy, ':');
427   if (ptr != NULL) {
428     *ptr++ = '\0';
429   }
430 
431   if (attr_id) {
432     *attr_id = atoi(var_cpy);
433   }
434 
435   if (attr_default) {
436     ptr = strchr(var, ':');
437 
438     /* Note: this works because the calling of this function by
439      * radius_have_var(), which occurs during the parsing process, uses
440      * a NULL for this portion, so that the string stored in the config_rec
441      * is not actually manipulated, as is done here.
442      */
443     if (var_len > 0) {
444       var[var_len-1] = '\0';
445     }
446 
447     if (ptr != NULL) {
448       *attr_default = ++ptr;
449     }
450   }
451 
452   /* Clean up. */
453   destroy_pool(tmp_pool);
454   return 0;
455 }
456 
radius_parse_gids_str(pool * p,char * gids_str,gid_t ** gids,unsigned int * ngids)457 static unsigned char radius_parse_gids_str(pool *p, char *gids_str,
458     gid_t **gids, unsigned int *ngids) {
459   char *val = NULL;
460   array_header *group_ids = make_array(p, 0, sizeof(gid_t));
461 
462   /* Add each GID to the array. */
463   while ((val = radius_argsep(&gids_str)) != NULL) {
464     gid_t gid;
465     char *endp = NULL;
466 
467     pr_signals_handle();
468 
469     /* Make sure the given ID is a valid number. */
470     gid = strtoul(val, &endp, 10);
471 
472     if (endp && *endp) {
473       pr_log_pri(PR_LOG_NOTICE, "RadiusGroupInfo badly formed group ID: %s",
474         val);
475       return FALSE;
476     }
477 
478     /* Push the ID into the ID array. */
479     *((gid_t *) push_array(group_ids)) = gid;
480   }
481 
482   *gids = (gid_t *) group_ids->elts;
483   *ngids = group_ids->nelts;
484 
485   return TRUE;
486 }
487 
radius_parse_groups_str(pool * p,char * groups_str,char *** groups,unsigned int * ngroups)488 static unsigned char radius_parse_groups_str(pool *p, char *groups_str,
489     char ***groups, unsigned int *ngroups) {
490   char *name = NULL;
491   array_header *group_names = make_array(p, 0, sizeof(char *));
492 
493   /* Add each name to the array. */
494   while ((name = radius_argsep(&groups_str)) != NULL) {
495     char *tmp;
496 
497     pr_signals_handle();
498     tmp = pstrdup(p, name);
499 
500     /* Push the name into the name array. */
501     *((char **) push_array(group_names)) = tmp;
502   }
503 
504   *groups = (char **) group_names->elts;
505   *ngroups = group_names->nelts;
506 
507   return TRUE;
508 }
509 
radius_process_standard_attribs(radius_packet_t * pkt,const unsigned char * secret,size_t secret_len)510 static int radius_process_standard_attribs(radius_packet_t *pkt,
511     const unsigned char *secret, size_t secret_len) {
512   int attrib_count = 0;
513   radius_attrib_t *attrib = NULL;
514   unsigned char attrib_len;
515 
516   pr_trace_msg(trace_channel, 2, "parsing packet for standard attribute IDs");
517 
518   if (radius_verify_auth_mac(pkt, "Access-Accept", secret, secret_len) < 0) {
519     return -1;
520   }
521 
522   /* TODO: Should we handle the Service-Type attribute here, make sure that it
523    * is a) a service type we implement, and b) the service type that we
524    * requested?
525    */
526 
527   /* Handle any CLASS attribute. */
528   if (!(radius_opts & RADIUS_OPT_IGNORE_CLASS_ATTR)) {
529     attrib = radius_get_attrib(pkt, RADIUS_CLASS);
530     if (attrib != NULL) {
531       attrib_len = RADIUS_ATTRIB_LEN(attrib);
532       if (attrib_len > 0) {
533         char *class = NULL;
534 
535         class = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
536         pr_trace_msg(trace_channel, 7,
537           "found Class attribute in Access-Accept message: %s", class);
538         radius_acct_class = class;
539         radius_acct_classlen = attrib_len;
540       }
541 
542       attrib_count++;
543 
544     } else {
545       pr_trace_msg(trace_channel, 6,
546         "Access-Accept packet lacks Class attribute (%d)", RADIUS_CLASS);
547     }
548   }
549 
550   /* Handle any User-Name attribute, per RFC 2865, Section 5.1. */
551   attrib = radius_get_attrib(pkt, RADIUS_USER_NAME);
552   if (attrib != NULL) {
553     attrib_len = RADIUS_ATTRIB_LEN(attrib);
554     if (attrib_len > 0) {
555       char *user_name = NULL;
556 
557       user_name = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
558       pr_trace_msg(trace_channel, 7,
559         "found User-Name attribute in Access-Accept message: %s", user_name);
560       radius_acct_user = user_name;
561       radius_acct_userlen = attrib_len;
562     }
563 
564     attrib_count++;
565 
566   } else {
567     pr_trace_msg(trace_channel, 6,
568       "Access-Accept packet lacks User-Name attribute (%d)", RADIUS_USER_NAME);
569   }
570 
571   /* Handle any REPLY_MESSAGE attributes. */
572   if (!(radius_opts & RADIUS_OPT_IGNORE_REPLY_MESSAGE_ATTR)) {
573     unsigned int pkt_len = 0;
574 
575     attrib = radius_get_next_attrib(pkt, RADIUS_REPLY_MESSAGE, &pkt_len, NULL);
576     while (attrib != NULL) {
577       pr_signals_handle();
578 
579       attrib_len = RADIUS_ATTRIB_LEN(attrib);
580       if (attrib_len > 0) {
581         char *reply_msg = NULL;
582 
583         reply_msg = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
584         pr_trace_msg(trace_channel, 7,
585           "found REPLY_MESSAGE attribute in Access-Accept message: '%s'",
586           reply_msg);
587         pr_response_add(R_DUP, "%s", reply_msg);
588       }
589 
590       attrib_count++;
591 
592       if (pkt_len == 0) {
593         break;
594       }
595 
596       attrib = radius_get_next_attrib(pkt, RADIUS_REPLY_MESSAGE, &pkt_len,
597         attrib);
598     }
599 
600     if (attrib_count == 0) {
601       pr_trace_msg(trace_channel, 6,
602         "Access-Accept packet lacks Reply-Message attribute (%d)",
603         RADIUS_REPLY_MESSAGE);
604     }
605   }
606 
607   /* Handle any IDLE_TIMEOUT attribute. */
608   if (!(radius_opts & RADIUS_OPT_IGNORE_IDLE_TIMEOUT_ATTR)) {
609     attrib = radius_get_attrib(pkt, RADIUS_IDLE_TIMEOUT);
610     if (attrib != NULL) {
611       attrib_len = RADIUS_ATTRIB_LEN(attrib);
612       if (attrib_len > 0) {
613         int timeout = -1;
614 
615         if (attrib_len > sizeof(timeout)) {
616           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
617             "invalid attribute length (%u) for Idle-Timeout, truncating",
618             attrib_len);
619           attrib_len = sizeof(timeout);
620         }
621 
622         memcpy(&timeout, attrib->data, attrib_len);
623         timeout = ntohl(timeout);
624 
625         if (timeout < 0) {
626           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
627             "packet includes Idle-Timeout attribute %d for illegal timeout: %d",
628             RADIUS_IDLE_TIMEOUT, timeout);
629 
630         } else {
631           config_rec *c;
632 
633           pr_trace_msg(trace_channel, 2,
634             "packet includes Idle-Timeout attribute %d for timeout: %d",
635             RADIUS_IDLE_TIMEOUT, timeout);
636           remove_config(main_server->conf, "TimeoutIdle", TRUE);
637 
638           c = pr_config_add_set(&main_server->conf, "TimeoutIdle",
639             PR_CONFIG_FL_INSERT_HEAD);
640           c->config_type = CONF_PARAM;
641           c->argc = 1;
642           c->argv[0] = palloc(c->pool, sizeof(int));
643           *((int *) c->argv[0]) = timeout;
644 
645           attrib_count++;
646         }
647       }
648 
649     } else {
650       pr_trace_msg(trace_channel, 6,
651         "Access-Accept packet lacks Idle-Timeout attribute (%d)",
652         RADIUS_IDLE_TIMEOUT);
653     }
654   }
655 
656   /* Handle any SESSION_TIMEOUT attribute. */
657   if (!(radius_opts & RADIUS_OPT_IGNORE_SESSION_TIMEOUT_ATTR)) {
658     attrib = radius_get_attrib(pkt, RADIUS_SESSION_TIMEOUT);
659     if (attrib != NULL) {
660       attrib_len = RADIUS_ATTRIB_LEN(attrib);
661       if (attrib_len > 0) {
662         int timeout = -1;
663 
664         if (attrib_len > sizeof(timeout)) {
665           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
666             "invalid attribute length (%u) for Session-Timeout, truncating",
667             attrib_len);
668           attrib_len = sizeof(timeout);
669         }
670 
671         memcpy(&timeout, attrib->data, attrib_len);
672         timeout = ntohl(timeout);
673 
674         if (timeout < 0) {
675           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
676             "packet includes Session-Timeout attribute %d for illegal "
677             "timeout: %d", RADIUS_SESSION_TIMEOUT, timeout);
678 
679         } else {
680           config_rec *c;
681 
682           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
683             "packet includes Session-Timeout attribute %d for timeout: %d",
684             RADIUS_SESSION_TIMEOUT, timeout);
685           remove_config(main_server->conf, "TimeoutSession", TRUE);
686 
687           c = pr_config_add_set(&main_server->conf, "TimeoutSession",
688             PR_CONFIG_FL_INSERT_HEAD);
689           c->config_type = CONF_PARAM;
690           c->argc = 2;
691           c->argv[0] = palloc(c->pool, sizeof(int));
692           *((int *) c->argv[0]) = timeout;
693           c->argv[1] = palloc(c->pool, sizeof(unsigned int));
694           *((unsigned int *) c->argv[1]) = 0;
695 
696           attrib_count++;
697         }
698       }
699 
700     } else {
701       pr_trace_msg(trace_channel, 6,
702         "Access-Accept packet lacks Session-Timeout attribute (%d)",
703         RADIUS_SESSION_TIMEOUT);
704     }
705   }
706 
707   return attrib_count;
708 }
709 
radius_process_user_info_attribs(radius_packet_t * pkt)710 static int radius_process_user_info_attribs(radius_packet_t *pkt) {
711   int attrib_count = 0;
712 
713   if (radius_uid_attr_id || radius_gid_attr_id ||
714       radius_home_attr_id || radius_shell_attr_id) {
715     pr_trace_msg(trace_channel, 2,
716       "parsing packet for RadiusUserInfo attributes");
717 
718     /* These custom values will been supplied in the configuration file, and
719      * set when the RadiusUserInfo config_rec is retrieved, during
720      * session initialization.
721      */
722 
723     if (radius_uid_attr_id) {
724       radius_attrib_t *attrib;
725 
726       attrib = radius_get_vendor_attrib(pkt, radius_uid_attr_id);
727       if (attrib) {
728         unsigned char attrib_len;
729         int uid = -1;
730 
731         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
732 
733         /* Parse the attribute value into an int, then cast it into the
734          * radius_passwd.pw_uid field.  Make sure it's a sane UID
735          * (ie non-negative).
736          */
737 
738         if (attrib_len > sizeof(uid)) {
739           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
740             "invalid attribute length (%u) for user ID, truncating",
741             attrib_len);
742           attrib_len = sizeof(uid);
743         }
744 
745         memcpy(&uid, attrib->data, attrib_len);
746         uid = ntohl(uid);
747 
748         if (uid < 0) {
749           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
750             "packet includes '%s' Vendor-Specific Attribute %d for illegal "
751             "user ID: %d", radius_vendor_name, radius_uid_attr_id, uid);
752 
753         } else {
754           radius_passwd.pw_uid = uid;
755 
756           pr_trace_msg(trace_channel, 3,
757             "packet includes '%s' Vendor-Specific Attribute %d for user ID: %d",
758             radius_vendor_name, radius_uid_attr_id,
759             radius_passwd.pw_uid);
760           attrib_count++;
761         }
762 
763       } else {
764         pr_trace_msg(trace_channel, 6,
765           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d "
766           "for user ID; defaulting to '%u'", radius_vendor_name,
767           radius_uid_attr_id, radius_passwd.pw_uid);
768       }
769     }
770 
771     if (radius_gid_attr_id) {
772       radius_attrib_t *attrib;
773 
774       attrib = radius_get_vendor_attrib(pkt, radius_gid_attr_id);
775       if (attrib) {
776         unsigned char attrib_len;
777         int gid = -1;
778 
779         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
780 
781         /* Parse the attribute value into an int, then cast it into the
782          * radius_passwd.pw_gid field.  Make sure it's a sane GID
783          * (ie non-negative).
784          */
785 
786         if (attrib_len > sizeof(gid)) {
787           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
788             "invalid attribute length (%u) for group ID, truncating",
789             attrib_len);
790           attrib_len = sizeof(gid);
791         }
792 
793         memcpy(&gid, attrib->data, attrib_len);
794         gid = ntohl(gid);
795 
796         if (gid < 0) {
797           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
798             "packet includes '%s' Vendor-Specific Attribute %d for illegal "
799             "group ID: %d", radius_vendor_name, radius_gid_attr_id, gid);
800 
801         } else {
802           radius_passwd.pw_gid = gid;
803 
804           pr_trace_msg(trace_channel, 3,
805             "packet includes '%s' Vendor-Specific Attribute %d for group "
806             "ID: %d", radius_vendor_name, radius_gid_attr_id,
807             radius_passwd.pw_gid);
808           attrib_count++;
809         }
810 
811       } else {
812         pr_trace_msg(trace_channel, 6,
813           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d "
814           "for group ID; defaulting to '%u'", radius_vendor_name,
815           radius_gid_attr_id, radius_passwd.pw_gid);
816       }
817     }
818 
819     if (radius_home_attr_id) {
820       radius_attrib_t *attrib;
821 
822       attrib = radius_get_vendor_attrib(pkt, radius_home_attr_id);
823       if (attrib) {
824         unsigned char attrib_len;
825         char *home;
826 
827         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
828 
829         home = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
830         if (*home != '/') {
831           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
832             "packet includes '%s' Vendor-Specific Attribute %d for illegal "
833             "home: '%s'", radius_vendor_name, radius_home_attr_id, home);
834 
835         } else {
836           radius_passwd.pw_dir = home;
837 
838           pr_trace_msg(trace_channel, 3,
839             "packet includes '%s' Vendor-Specific Attribute %d for home "
840             "directory: '%s'", radius_vendor_name, radius_home_attr_id,
841             radius_passwd.pw_dir);
842           attrib_count++;
843         }
844 
845       } else {
846         pr_trace_msg(trace_channel, 6,
847           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
848           "home directory; defaulting to '%s'", radius_vendor_name,
849           radius_home_attr_id, radius_passwd.pw_dir);
850       }
851     }
852 
853     if (radius_shell_attr_id) {
854       radius_attrib_t *attrib;
855 
856       attrib = radius_get_vendor_attrib(pkt, radius_shell_attr_id);
857       if (attrib) {
858         unsigned char attrib_len;
859         char *shell;
860 
861         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
862 
863         shell = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
864         if (*shell != '/') {
865           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
866             "packet includes '%s' Vendor-Specific Attribute %d for illegal "
867             "shell: '%s'", radius_vendor_name, radius_shell_attr_id, shell);
868 
869         } else {
870           radius_passwd.pw_shell = shell;
871 
872           pr_trace_msg(trace_channel, 3,
873             "packet includes '%s' Vendor-Specific Attribute %d for "
874             "shell: '%s'", radius_vendor_name, radius_shell_attr_id,
875             radius_passwd.pw_shell);
876           attrib_count++;
877         }
878 
879       } else {
880         pr_trace_msg(trace_channel, 6,
881           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
882           "shell; defaulting to '%s'", radius_vendor_name, radius_shell_attr_id,
883           radius_passwd.pw_shell);
884       }
885     }
886   }
887 
888   return attrib_count;
889 }
890 
radius_process_group_info_attribs(radius_packet_t * pkt)891 static int radius_process_group_info_attribs(radius_packet_t *pkt) {
892   int attrib_count = 0;
893 
894   if (radius_prime_group_name_attr_id ||
895       radius_addl_group_names_attr_id ||
896       radius_addl_group_ids_attr_id) {
897     unsigned int ngroups = 0, ngids = 0;
898     char **groups = NULL;
899     gid_t *gids = NULL;
900 
901     pr_trace_msg(trace_channel, 2,
902       "parsing packet for RadiusGroupInfo attributes");
903 
904     if (radius_prime_group_name_attr_id) {
905       radius_attrib_t *attrib;
906 
907       attrib = radius_get_vendor_attrib(pkt, radius_prime_group_name_attr_id);
908       if (attrib) {
909         unsigned char attrib_len;
910         char *group_name;
911 
912         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
913 
914         group_name = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
915         radius_prime_group_name = pstrdup(radius_pool, group_name);
916 
917         pr_trace_msg(trace_channel, 3,
918           "packet includes '%s' Vendor-Specific Attribute %d for primary "
919           "group name: '%s'", radius_vendor_name,
920           radius_prime_group_name_attr_id, radius_prime_group_name);
921         attrib_count++;
922 
923       } else {
924         pr_trace_msg(trace_channel, 6,
925           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
926           "prime group name; defaulting to '%s'", radius_vendor_name,
927           radius_prime_group_name_attr_id, radius_prime_group_name);
928       }
929     }
930 
931     if (radius_addl_group_names_attr_id) {
932       radius_attrib_t *attrib;
933 
934       attrib = radius_get_vendor_attrib(pkt, radius_addl_group_names_attr_id);
935       if (attrib) {
936         unsigned char attrib_len;
937         char *group_names, *group_names_str;
938 
939         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
940         group_names = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
941 
942         /* Make a copy of the string, for parsing purposes.  The parsing
943          * of this string will consume it.
944          */
945         group_names_str = pstrdup(radius_pool, group_names);
946 
947         if (!radius_parse_groups_str(radius_pool, group_names_str, &groups,
948             &ngroups)) {
949           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
950             "packet includes '%s' Vendor-Specific Attribute %d for illegal "
951             "additional group names: '%s'", radius_vendor_name,
952             radius_addl_group_names_attr_id, group_names);
953 
954         } else {
955           pr_trace_msg(trace_channel, 3,
956             "packet includes '%s' Vendor-Specific Attribute %d for "
957             "additional group names: '%s'", radius_vendor_name,
958             radius_addl_group_names_attr_id, group_names);
959         }
960 
961         attrib_count++;
962 
963       } else {
964         pr_trace_msg(trace_channel, 6,
965           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
966           "additional group names; defaulting to '%s'", radius_vendor_name,
967           radius_addl_group_names_attr_id, radius_addl_group_names_str);
968       }
969     }
970 
971     if (radius_addl_group_ids_attr_id) {
972       radius_attrib_t *attrib;
973 
974       attrib = radius_get_vendor_attrib(pkt, radius_addl_group_ids_attr_id);
975       if (attrib) {
976         unsigned char attrib_len;
977         char *group_ids, *group_ids_str;
978 
979         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
980         group_ids = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
981 
982         /* Make a copy of the string, for parsing purposes.  The parsing
983          * of this string will consume it.
984          */
985         group_ids_str = pstrdup(radius_pool, group_ids);
986 
987         if (!radius_parse_gids_str(radius_pool, group_ids_str, &gids, &ngids)) {
988           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
989             "packet includes '%s' Vendor-Specific Attribute %d for illegal "
990             "additional group IDs: '%s'", radius_vendor_name,
991             radius_addl_group_ids_attr_id, group_ids);
992 
993         } else {
994           pr_trace_msg(trace_channel, 3,
995             "packet includes '%s' Vendor-Specific Attribute %d for additional "
996             "group IDs: '%s'", radius_vendor_name,
997             radius_addl_group_ids_attr_id, group_ids);
998         }
999 
1000         attrib_count++;
1001 
1002       } else {
1003         pr_trace_msg(trace_channel, 6,
1004           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1005           "additional group IDs; defaulting to '%s'", radius_vendor_name,
1006           radius_addl_group_ids_attr_id, radius_addl_group_ids_str);
1007       }
1008     }
1009 
1010     /* One last RadiusGroupInfo check: does the number of returned group
1011      * names match the number of returned group IDs?
1012      */
1013     if (ngroups == ngids) {
1014       radius_have_group_info = TRUE;
1015       radius_addl_group_count = ngroups;
1016       radius_addl_group_names = groups;
1017       radius_addl_group_ids = gids;
1018 
1019     } else {
1020       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1021         "server provided mismatched number of group names (%u) and group "
1022         "IDs (%u), ignoring them", ngroups, ngids);
1023     }
1024   }
1025 
1026   return attrib_count;
1027 }
1028 
radius_process_quota_info_attribs(radius_packet_t * pkt)1029 static int radius_process_quota_info_attribs(radius_packet_t *pkt) {
1030   int attrib_count = 0;
1031 
1032   if (radius_quota_per_sess_attr_id ||
1033       radius_quota_limit_type_attr_id ||
1034       radius_quota_bytes_in_attr_id ||
1035       radius_quota_bytes_out_attr_id ||
1036       radius_quota_bytes_xfer_attr_id ||
1037       radius_quota_files_in_attr_id ||
1038       radius_quota_files_out_attr_id ||
1039       radius_quota_files_xfer_attr_id) {
1040 
1041     pr_trace_msg(trace_channel, 2,
1042       "parsing packet for RadiusQuotaInfo attributes");
1043 
1044     if (radius_quota_per_sess_attr_id) {
1045       radius_attrib_t *attrib;
1046 
1047       attrib = radius_get_vendor_attrib(pkt, radius_quota_per_sess_attr_id);
1048       if (attrib) {
1049         unsigned char attrib_len;
1050         char *per_sess;
1051 
1052         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1053         per_sess = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1054 
1055         radius_quota_per_sess = per_sess;
1056 
1057         pr_trace_msg(trace_channel, 2,
1058           "packet includes '%s' Vendor-Specific Attribute %d for quota "
1059           "per-session: '%s'", radius_vendor_name,
1060           radius_quota_per_sess_attr_id, radius_quota_per_sess);
1061         attrib_count++;
1062 
1063       } else {
1064         pr_trace_msg(trace_channel, 6,
1065           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1066           "quota per-session; defaulting to '%s'", radius_vendor_name,
1067           radius_quota_per_sess_attr_id, radius_quota_per_sess);
1068       }
1069     }
1070 
1071     if (radius_quota_limit_type_attr_id) {
1072       radius_attrib_t *attrib;
1073 
1074       attrib = radius_get_vendor_attrib(pkt, radius_quota_limit_type_attr_id);
1075       if (attrib) {
1076         unsigned char attrib_len;
1077         char *limit_type;
1078 
1079         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1080         limit_type = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1081         radius_quota_limit_type = limit_type;
1082 
1083         pr_trace_msg(trace_channel, 2,
1084           "packet includes '%s' Vendor-Specific Attribute %d for quota limit "
1085           "type: '%s'", radius_vendor_name, radius_quota_limit_type_attr_id,
1086           radius_quota_limit_type);
1087         attrib_count++;
1088 
1089       } else {
1090         pr_trace_msg(trace_channel, 6,
1091           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1092           "quota limit type; defaulting to '%s'", radius_vendor_name,
1093           radius_quota_limit_type_attr_id, radius_quota_limit_type);
1094       }
1095     }
1096 
1097     if (radius_quota_bytes_in_attr_id) {
1098       radius_attrib_t *attrib;
1099 
1100       attrib = radius_get_vendor_attrib(pkt, radius_quota_bytes_in_attr_id);
1101       if (attrib) {
1102         unsigned char attrib_len;
1103         char *bytes_in;
1104 
1105         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1106         bytes_in = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1107         radius_quota_bytes_in = bytes_in;
1108 
1109         pr_trace_msg(trace_channel, 3,
1110           "packet includes '%s' Vendor-Specific Attribute %d for quota bytes "
1111           "in available: '%s'", radius_vendor_name,
1112           radius_quota_bytes_in_attr_id, radius_quota_bytes_in);
1113         attrib_count++;
1114 
1115       } else {
1116         pr_trace_msg(trace_channel, 6,
1117           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1118           "quota bytes in available; defaulting to '%s'", radius_vendor_name,
1119           radius_quota_bytes_in_attr_id, radius_quota_bytes_in);
1120       }
1121     }
1122 
1123     if (radius_quota_bytes_out_attr_id) {
1124       radius_attrib_t *attrib;
1125 
1126       attrib = radius_get_vendor_attrib(pkt, radius_quota_bytes_out_attr_id);
1127       if (attrib) {
1128         unsigned char attrib_len;
1129         char *bytes_out;
1130 
1131         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1132         bytes_out = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1133         radius_quota_bytes_out = bytes_out;
1134 
1135         pr_trace_msg(trace_channel, 3,
1136           "packet includes '%s' Vendor-Specific Attribute %d for quota bytes "
1137           "out available: '%s'", radius_vendor_name,
1138           radius_quota_bytes_out_attr_id, radius_quota_bytes_out);
1139         attrib_count++;
1140 
1141       } else {
1142         pr_trace_msg(trace_channel, 6,
1143           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1144           "quota bytes out available; defaulting to '%s'", radius_vendor_name,
1145           radius_quota_bytes_out_attr_id, radius_quota_bytes_out);
1146       }
1147     }
1148 
1149     if (radius_quota_bytes_xfer_attr_id) {
1150       radius_attrib_t *attrib;
1151 
1152       attrib = radius_get_vendor_attrib(pkt, radius_quota_bytes_xfer_attr_id);
1153       if (attrib) {
1154         unsigned char attrib_len;
1155         char *bytes_xfer;
1156 
1157         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1158         bytes_xfer = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1159         radius_quota_bytes_xfer = bytes_xfer;
1160 
1161         pr_trace_msg(trace_channel, 3,
1162           "packet includes '%s' Vendor-Specific Attribute %d for quota bytes "
1163           "xfer available: '%s'", radius_vendor_name,
1164           radius_quota_bytes_xfer_attr_id, radius_quota_bytes_xfer);
1165         attrib_count++;
1166 
1167       } else {
1168         pr_trace_msg(trace_channel, 6,
1169           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1170           "quota bytes xfer available; defaulting to '%s'", radius_vendor_name,
1171           radius_quota_bytes_xfer_attr_id, radius_quota_bytes_xfer);
1172       }
1173     }
1174 
1175     if (radius_quota_files_in_attr_id) {
1176       radius_attrib_t *attrib;
1177 
1178       attrib = radius_get_vendor_attrib(pkt, radius_quota_files_in_attr_id);
1179       if (attrib) {
1180         unsigned char attrib_len;
1181         char *files_in;
1182 
1183         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1184         files_in = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1185         radius_quota_files_in = files_in;
1186 
1187         pr_trace_msg(trace_channel, 3,
1188           "packet includes '%s' Vendor-Specific Attribute %d for quota files "
1189           "in available: '%s'", radius_vendor_name,
1190           radius_quota_files_in_attr_id, radius_quota_files_in);
1191         attrib_count++;
1192 
1193       } else {
1194         pr_trace_msg(trace_channel, 6,
1195           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1196           "quota files in available; defaulting to '%s'", radius_vendor_name,
1197           radius_quota_files_in_attr_id, radius_quota_files_in);
1198       }
1199     }
1200 
1201     if (radius_quota_files_out_attr_id) {
1202       radius_attrib_t *attrib;
1203 
1204       attrib = radius_get_vendor_attrib(pkt, radius_quota_files_out_attr_id);
1205       if (attrib) {
1206         unsigned char attrib_len;
1207         char *files_out;
1208 
1209         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1210         files_out = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1211         radius_quota_files_out = files_out;
1212 
1213         pr_trace_msg(trace_channel, 3,
1214           "packet includes '%s' Vendor-Specific Attribute %d for quota files "
1215           "out available: '%s'", radius_vendor_name,
1216           radius_quota_files_out_attr_id, radius_quota_files_out);
1217         attrib_count++;
1218 
1219       } else {
1220         pr_trace_msg(trace_channel, 6,
1221           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1222           "quota files out available; defaulting to '%s'", radius_vendor_name,
1223           radius_quota_files_out_attr_id, radius_quota_files_out);
1224       }
1225     }
1226 
1227     if (radius_quota_files_xfer_attr_id) {
1228       radius_attrib_t *attrib;
1229 
1230       attrib = radius_get_vendor_attrib(pkt, radius_quota_files_xfer_attr_id);
1231       if (attrib) {
1232         unsigned char attrib_len;
1233         char *files_xfer;
1234 
1235         attrib_len = RADIUS_VSA_ATTRIB_LEN(attrib);
1236         files_xfer = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1237         radius_quota_files_xfer = files_xfer;
1238 
1239         pr_trace_msg(trace_channel, 3,
1240           "packet includes '%s' Vendor-Specific Attribute %d for quota files "
1241           "xfer available: '%s'", radius_vendor_name,
1242           radius_quota_files_xfer_attr_id, radius_quota_files_xfer);
1243         attrib_count++;
1244 
1245       } else {
1246         pr_trace_msg(trace_channel, 6,
1247           "Access-Accept packet lacks '%s' Vendor-Specific Attribute %d for "
1248           "quota files xfer available; defaulting to '%s'", radius_vendor_name,
1249           radius_quota_files_xfer_attr_id, radius_quota_files_xfer);
1250       }
1251     }
1252   }
1253 
1254   return attrib_count;
1255 }
1256 
radius_process_accept_packet(radius_packet_t * pkt,const unsigned char * secret,size_t secret_len)1257 static int radius_process_accept_packet(radius_packet_t *pkt,
1258     const unsigned char *secret, size_t secret_len) {
1259   int attrib_count = 0, res;;
1260 
1261   res = radius_process_standard_attribs(pkt, secret, secret_len);
1262   if (res < 0) {
1263     return -1;
1264   }
1265 
1266   attrib_count += res;
1267 
1268   /* Now, parse the packet for any server-supplied RadiusUserInfo attributes,
1269    * if RadiusUserInfo is indeed in effect.
1270    */
1271 
1272   if (radius_have_user_info == FALSE &&
1273       radius_have_group_info == FALSE &&
1274       radius_have_quota_info == FALSE) {
1275     /* Return now if there's no reason for doing extra work. */
1276     return attrib_count;
1277   }
1278 
1279   attrib_count += radius_process_user_info_attribs(pkt);
1280   attrib_count += radius_process_group_info_attribs(pkt);
1281   attrib_count += radius_process_quota_info_attribs(pkt);
1282 
1283   return attrib_count;
1284 }
1285 
radius_process_reject_packet(radius_packet_t * pkt,const unsigned char * secret,size_t secret_len)1286 static int radius_process_reject_packet(radius_packet_t *pkt,
1287     const unsigned char *secret, size_t secret_len) {
1288   int attrib_count = 0;
1289 
1290   if (radius_verify_auth_mac(pkt, "Access-Reject", secret, secret_len) < 0) {
1291     return -1;
1292   }
1293 
1294   /* Handle any REPLY_MESSAGE attributes. */
1295   if (!(radius_opts & RADIUS_OPT_IGNORE_REPLY_MESSAGE_ATTR)) {
1296     radius_attrib_t *attrib = NULL;
1297     unsigned int pkt_len = 0;
1298 
1299     attrib = radius_get_next_attrib(pkt, RADIUS_REPLY_MESSAGE, &pkt_len,
1300       NULL);
1301     while (attrib != NULL) {
1302       unsigned char attrib_len;
1303 
1304       pr_signals_handle();
1305 
1306       attrib_len = RADIUS_ATTRIB_LEN(attrib);
1307       if (attrib_len > 0) {
1308         char *reply_msg = NULL;
1309 
1310         reply_msg = pstrndup(radius_pool, (char *) attrib->data, attrib_len);
1311 
1312         pr_trace_msg(trace_channel, 7,
1313           "found REPLY_MESSAGE attribute in Access-Reject message: '%s'",
1314           reply_msg);
1315         pr_response_add_err(R_DUP, "%s", reply_msg);
1316       }
1317 
1318       attrib_count++;
1319 
1320       if (pkt_len == 0) {
1321         break;
1322       }
1323 
1324       attrib = radius_get_next_attrib(pkt, RADIUS_REPLY_MESSAGE, &pkt_len,
1325         attrib);
1326     }
1327   }
1328 
1329   return attrib_count;
1330 }
1331 
radius_process_group_info(config_rec * c)1332 static void radius_process_group_info(config_rec *c) {
1333   char *param = NULL;
1334   unsigned char have_illegal_value = FALSE;
1335   unsigned int ngroups = 0, ngids = 0;
1336   char **groups = NULL;
1337   gid_t *gids = NULL;
1338 
1339   /* Parse out any configured attribute/defaults here. The stored strings will
1340    * already have been sanitized by the configuration handler, so I don't
1341    * need to worry about that here.
1342    */
1343 
1344   param = (char *) c->argv[0];
1345   if (RADIUS_IS_VAR(param) == TRUE) {
1346     radius_parse_var(param, &radius_prime_group_name_attr_id,
1347       &radius_prime_group_name);
1348 
1349   } else {
1350     radius_prime_group_name = param;
1351   }
1352 
1353   /* If the group name count is zero, then I know that the data will be
1354    * contained in a VSA.  Otherwise, the group names have already been parsed.
1355    */
1356   if (*((unsigned int *) c->argv[1]) == 0) {
1357     param = (char *) c->argv[2];
1358 
1359     radius_parse_var(param, &radius_addl_group_names_attr_id,
1360       &radius_addl_group_names_str);
1361 
1362     /* Now, parse the default value provided. */
1363     if (!radius_parse_groups_str(c->pool, radius_addl_group_names_str,
1364         &groups, &ngroups)) {
1365       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1366         "badly formatted RadiusGroupInfo default additional group names");
1367       have_illegal_value = TRUE;
1368     }
1369 
1370   } else {
1371     ngroups = *((unsigned int *) c->argv[1]);
1372     groups = (char **) c->argv[2];
1373   }
1374 
1375   if (*((unsigned int *) c->argv[3]) == 0) {
1376     param = (char *) c->argv[4];
1377 
1378     radius_parse_var(param, &radius_addl_group_ids_attr_id,
1379       &radius_addl_group_ids_str);
1380 
1381     /* Similarly, parse the default value provided. */
1382     if (!radius_parse_gids_str(c->pool, radius_addl_group_ids_str,
1383         &gids, &ngids)) {
1384       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1385         "badly formatted RadiusGroupInfo default additional group IDs");
1386       have_illegal_value = TRUE;
1387     }
1388 
1389   } else {
1390     ngids = *((unsigned int *) c->argv[3]);
1391     gids = (gid_t *) c->argv[4];
1392   }
1393 
1394   if (!have_illegal_value &&
1395       ngroups != ngids) {
1396     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1397       "mismatched number of RadiusGroupInfo default additional group "
1398       "names (%u) and IDs (%u)", ngroups, ngids);
1399     have_illegal_value = TRUE;
1400   }
1401 
1402   if (!have_illegal_value) {
1403     radius_have_group_info = TRUE;
1404     radius_addl_group_count = ngroups;
1405     radius_addl_group_names = groups;
1406     radius_addl_group_ids = gids;
1407 
1408   } else {
1409     radius_have_group_info = FALSE;
1410     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1411       "error with RadiusGroupInfo parameters, ignoring them");
1412   }
1413 }
1414 
radius_process_quota_info(config_rec * c)1415 static void radius_process_quota_info(config_rec *c) {
1416   char *param = NULL;
1417   unsigned char have_illegal_value = FALSE;
1418 
1419   /* Parse out any configured attribute/defaults here. The stored strings will
1420    * already have been sanitized by the configuration handler, so I don't
1421    * need to worry about that here.
1422    */
1423 
1424   param = (char *) c->argv[0];
1425   if (RADIUS_IS_VAR(param) == TRUE) {
1426     radius_parse_var(param, &radius_quota_per_sess_attr_id,
1427       &radius_quota_per_sess);
1428 
1429   } else {
1430     radius_quota_per_sess = param;
1431 
1432     if (strcasecmp(param, "false") != 0 &&
1433         strcasecmp(param, "true") != 0) {
1434       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1435         "illegal RadiusQuotaInfo per-session value: '%s'", param);
1436       have_illegal_value = TRUE;
1437 
1438     } else {
1439       pr_trace_msg(trace_channel, 17,
1440         "found RadiusQuotaInfo per-session value: %s", param);
1441     }
1442   }
1443 
1444   param = (char *) c->argv[1];
1445   if (RADIUS_IS_VAR(param) == TRUE) {
1446     radius_parse_var(param, &radius_quota_limit_type_attr_id,
1447       &radius_quota_limit_type);
1448 
1449   } else {
1450     radius_quota_limit_type = param;
1451 
1452     if (strcasecmp(param, "hard") != 0 &&
1453         strcasecmp(param, "soft") != 0) {
1454       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1455         "illegal RadiusQuotaInfo limit type value: '%s'", param);
1456       have_illegal_value = TRUE;
1457 
1458     } else {
1459       pr_trace_msg(trace_channel, 17,
1460         "found RadiusQuotaInfo limit type value: %s", param);
1461     }
1462   }
1463 
1464   param = (char *) c->argv[2];
1465   if (RADIUS_IS_VAR(param) == TRUE) {
1466     radius_parse_var(param, &radius_quota_bytes_in_attr_id,
1467       &radius_quota_bytes_in);
1468 
1469   } else {
1470     char *endp = NULL;
1471 
1472     if (strtod(param, &endp) < 0) {
1473       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1474         "illegal RadiusQuotaInfo bytes in value: negative number");
1475       have_illegal_value = TRUE;
1476     }
1477 
1478     if (endp && *endp) {
1479       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1480         "illegal RadiusQuotaInfo bytes in value: '%s' not a number", param);
1481       have_illegal_value = TRUE;
1482 
1483     } else {
1484       pr_trace_msg(trace_channel, 17,
1485         "found RadiusQuotaInfo bytes in value: %s", param);
1486     }
1487 
1488     radius_quota_bytes_in = param;
1489   }
1490 
1491   param = (char *) c->argv[3];
1492   if (RADIUS_IS_VAR(param) == TRUE) {
1493     radius_parse_var(param, &radius_quota_bytes_out_attr_id,
1494       &radius_quota_bytes_out);
1495 
1496   } else {
1497     char *endp = NULL;
1498 
1499     if (strtod(param, &endp) < 0) {
1500       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1501         "illegal RadiusQuotaInfo bytes out value: negative number");
1502       have_illegal_value = TRUE;
1503     }
1504 
1505     if (endp && *endp) {
1506       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1507         "illegal RadiusQuotaInfo bytes out value: '%s' not a number", param);
1508       have_illegal_value = TRUE;
1509 
1510     } else {
1511       pr_trace_msg(trace_channel, 17,
1512         "found RadiusQuotaInfo bytes out value: %s", param);
1513     }
1514 
1515     radius_quota_bytes_out = param;
1516   }
1517 
1518   param = (char *) c->argv[4];
1519   if (RADIUS_IS_VAR(param) == TRUE) {
1520     radius_parse_var(param, &radius_quota_bytes_xfer_attr_id,
1521       &radius_quota_bytes_xfer);
1522 
1523   } else {
1524     char *endp = NULL;
1525 
1526     if (strtod(param, &endp) < 0) {
1527       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1528         "illegal RadiusQuotaInfo bytes xfer value: negative number");
1529       have_illegal_value = TRUE;
1530     }
1531 
1532     if (endp && *endp) {
1533       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1534         "illegal RadiusQuotaInfo bytes xfer value: '%s' not a number", param);
1535       have_illegal_value = TRUE;
1536 
1537     } else {
1538       pr_trace_msg(trace_channel, 17,
1539         "found RadiusQuotaInfo bytes xfer value: %s", param);
1540     }
1541 
1542     radius_quota_bytes_xfer = param;
1543   }
1544 
1545   param = (char *) c->argv[5];
1546   if (RADIUS_IS_VAR(param) == TRUE) {
1547     radius_parse_var(param, &radius_quota_files_in_attr_id,
1548       &radius_quota_files_in);
1549 
1550   } else {
1551     char *endp = NULL;
1552     unsigned long res;
1553 
1554     res = strtoul(param, &endp, 10);
1555     if (endp && *endp) {
1556       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1557         "illegal RadiusQuotaInfo files in value: '%s' not a number",
1558         param);
1559       have_illegal_value = TRUE;
1560 
1561     } else {
1562       pr_trace_msg(trace_channel, 17,
1563         "found RadiusQuotaInfo files in value: %lu", res);
1564     }
1565 
1566     radius_quota_files_in = param;
1567   }
1568 
1569   param = (char *) c->argv[6];
1570   if (RADIUS_IS_VAR(param) == TRUE) {
1571     radius_parse_var(param, &radius_quota_files_out_attr_id,
1572       &radius_quota_files_out);
1573 
1574   } else {
1575     char *endp = NULL;
1576     unsigned long res;
1577 
1578     res = strtoul(param, &endp, 10);
1579     if (endp && *endp) {
1580       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1581         "illegal RadiusQuotaInfo files out value: '%s' not a number", param);
1582       have_illegal_value = TRUE;
1583 
1584     } else {
1585       pr_trace_msg(trace_channel, 17,
1586         "found RadiusQuotaInfo files out value: %lu", res);
1587     }
1588 
1589     radius_quota_files_out = param;
1590   }
1591 
1592   param = (char *) c->argv[7];
1593   if (RADIUS_IS_VAR(param) == TRUE) {
1594     radius_parse_var(param, &radius_quota_files_xfer_attr_id,
1595       &radius_quota_files_xfer);
1596 
1597   } else {
1598     char *endp = NULL;
1599     unsigned long res;
1600 
1601     res = strtoul(param, &endp, 10);
1602     if (endp && *endp) {
1603       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1604         "illegal RadiusQuotaInfo files xfer value: '%s' not a number", param);
1605       have_illegal_value = TRUE;
1606 
1607     } else {
1608       pr_trace_msg(trace_channel, 17,
1609         "found RadiusQuotaInfo files xfer value: %lu", res);
1610     }
1611 
1612     radius_quota_files_xfer = param;
1613   }
1614 
1615   if (!have_illegal_value) {
1616     radius_have_quota_info = TRUE;
1617 
1618   } else {
1619    (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1620      "error with RadiusQuotaInfo parameters, ignoring them");
1621   }
1622 }
1623 
radius_process_user_info(config_rec * c)1624 static void radius_process_user_info(config_rec *c) {
1625   char *param = NULL;
1626   unsigned char have_illegal_value = FALSE;
1627 
1628   /* radius_passwd.pw_name will be filled in later, after successful
1629    * authentication.  radius_passwd.pw_gecos will always be NULL, as there
1630    * is no practical need for this information.
1631    */
1632 
1633   radius_passwd.pw_passwd = NULL;
1634   radius_passwd.pw_gecos = NULL;
1635 
1636   /* Parse out any configured attribute/defaults here. The stored strings will
1637    * already have been sanitized by the configuration handler, so I don't
1638    * need to worry about that here.
1639    */
1640 
1641   /* Process the UID string. */
1642   param = (char *) c->argv[0];
1643 
1644   if (RADIUS_IS_VAR(param) == TRUE) {
1645     char *endp = NULL, *value = NULL;
1646 
1647     radius_parse_var(param, &radius_uid_attr_id, &value);
1648     radius_passwd.pw_uid = (uid_t) strtoul(value, &endp, 10);
1649 
1650     if (radius_passwd.pw_uid == (uid_t) -1) {
1651       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1652         "illegal RadiusUserInfo default UID value: -1 not allowed");
1653       have_illegal_value = TRUE;
1654     }
1655 
1656     if (endp && *endp) {
1657       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1658         "illegal RadiusUserInfo default UID value: '%s' not a number", value);
1659       have_illegal_value = TRUE;
1660     }
1661 
1662   } else {
1663 
1664     char *endp = NULL;
1665     radius_passwd.pw_uid = (uid_t) strtoul(param, &endp, 10);
1666 
1667     if (radius_passwd.pw_uid == (uid_t) -1) {
1668       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1669         "illegal RadiusUserInfo UID value: -1 not allowed");
1670       have_illegal_value = TRUE;
1671     }
1672 
1673     if (endp && *endp) {
1674       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1675         "illegal RadiusUserInfo UID value: '%s' not a number", param);
1676       have_illegal_value = TRUE;
1677     }
1678   }
1679 
1680   /* Process the GID string. */
1681   param = (char *) c->argv[1];
1682 
1683   if (RADIUS_IS_VAR(param) == TRUE) {
1684     char *endp = NULL, *value = NULL;
1685 
1686     radius_parse_var(param, &radius_gid_attr_id, &value);
1687     radius_passwd.pw_gid = (gid_t) strtoul(value, &endp, 10);
1688 
1689     if (radius_passwd.pw_gid == (gid_t) -1) {
1690       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1691         "illegal RadiusUserInfo default GID value: -1 not allowed");
1692       have_illegal_value = TRUE;
1693     }
1694 
1695     if (endp && *endp) {
1696       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1697         "illegal RadiusUserInfo default GID value: '%s' not a number", value);
1698       have_illegal_value = TRUE;
1699     }
1700 
1701   } else {
1702 
1703     char *endp = NULL;
1704     radius_passwd.pw_gid = (gid_t) strtoul(param, &endp, 10);
1705 
1706     if (radius_passwd.pw_gid == (gid_t) -1) {
1707       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1708         "illegal RadiusUserInfo GID value: -1 not allowed");
1709       have_illegal_value = TRUE;
1710     }
1711 
1712     if (endp && *endp) {
1713       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1714         "illegal RadiusUserInfo GID value: '%s' not a number", param);
1715       have_illegal_value = TRUE;
1716     }
1717   }
1718 
1719   /* Parse the home directory string. */
1720   param = (char *) c->argv[2];
1721 
1722   if (RADIUS_IS_VAR(param) == TRUE) {
1723     radius_parse_var(param, &radius_home_attr_id, &radius_passwd.pw_dir);
1724 
1725     if (*radius_passwd.pw_dir != '/') {
1726       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1727         "illegal RadiusUserInfo default home value: '%s' not an absolute path",
1728         radius_passwd.pw_dir);
1729       have_illegal_value = TRUE;
1730     }
1731 
1732   } else {
1733 
1734     /* Param already checked in this case. */
1735     radius_passwd.pw_dir = param;
1736   }
1737 
1738   /* Process the shell string. */
1739   param = (char *) c->argv[3];
1740 
1741   if (RADIUS_IS_VAR(param) == TRUE) {
1742     radius_parse_var(param, &radius_shell_attr_id, &radius_passwd.pw_shell);
1743 
1744     if (*radius_passwd.pw_shell != '/') {
1745       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1746         "illegal RadiusUserInfo default shell value: '%s' not an absolute path",
1747         radius_passwd.pw_shell);
1748       have_illegal_value = TRUE;
1749     }
1750 
1751   } else {
1752 
1753     /* Param already checked in this case. */
1754     radius_passwd.pw_shell = param;
1755   }
1756 
1757   if (have_illegal_value == FALSE) {
1758     radius_have_user_info = TRUE;
1759 
1760   } else {
1761     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
1762       "error with RadiusUserInfo parameters, ignoring them");
1763   }
1764 }
1765 
radius_reset(void)1766 static void radius_reset(void) {
1767   /* Clear/reset user info */
1768   radius_have_user_info = FALSE;
1769 
1770   /* Clear/reset group info */
1771   radius_have_group_info = FALSE;
1772   radius_prime_group_name = NULL;
1773   radius_addl_group_count = 0;
1774   radius_addl_group_names = NULL;
1775   radius_addl_group_names_str = NULL;
1776   radius_addl_group_ids = NULL;
1777   radius_addl_group_ids_str = NULL;
1778 
1779   /* Clear/reset quota info */
1780   radius_have_quota_info = FALSE;
1781   radius_quota_per_sess = NULL;
1782   radius_quota_limit_type = NULL;
1783   radius_quota_bytes_in = NULL;
1784   radius_quota_bytes_out = NULL;
1785   radius_quota_bytes_xfer = NULL;
1786   radius_quota_files_in = NULL;
1787   radius_quota_files_out = NULL;
1788 
1789   /* Clear/reset quota info */
1790   radius_have_quota_info = FALSE;
1791   radius_quota_per_sess = NULL;
1792   radius_quota_limit_type = NULL;
1793   radius_quota_bytes_in = NULL;
1794   radius_quota_bytes_out = NULL;
1795   radius_quota_bytes_xfer = NULL;
1796   radius_quota_files_in = NULL;
1797   radius_quota_files_out = NULL;
1798   radius_quota_files_xfer = NULL;
1799 
1800   /* Clear/reset other info */
1801   radius_have_other_info = FALSE;
1802 }
1803 
radius_xor(unsigned char * p,unsigned char * q,size_t len)1804 static unsigned char *radius_xor(unsigned char *p, unsigned char *q,
1805     size_t len) {
1806   register size_t i = 0;
1807   unsigned char *tmp = p;
1808 
1809   for (i = 0; i < len; i++) {
1810     *(p++) ^= *(q++);
1811   }
1812 
1813   return tmp;
1814 }
1815 
1816 #if defined(PR_USE_OPENSSL)
1817 # include <openssl/err.h>
1818 # include <openssl/md5.h>
1819 # include <openssl/hmac.h>
1820 
1821 #else
1822 /* Built-in MD5 */
1823 
1824 /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991.
1825  *  All rights reserved.
1826  *
1827  * License to copy and use this software is granted provided that it
1828  * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
1829  * Algorithm" in all material mentioning or referencing this software
1830  * or this function.
1831  *
1832  * License is also granted to make and use derivative works provided
1833  * that such works are identified as "derived from the RSA Data
1834  * Security, Inc. MD5 Message-Digest Algorithm" in all material
1835  * mentioning or referencing the derived work.
1836  *
1837  * RSA Data Security, Inc. makes no representations concerning either
1838  * the merchantability of this software or the suitability of this
1839  * software for any particular purpose. It is provided "as is"
1840  * without express or implied warranty of any kind.
1841  *
1842  * These notices must be retained in any copies of any part of this
1843  * documentation and/or software.
1844  */
1845 
1846 /* MD5 context */
1847 typedef struct {
1848 
1849   /* state (ABCD) */
1850   uint32_t state[4];
1851 
1852   /* number of bits, module 2^64 (LSB first) */
1853   uint32_t count[2];
1854 
1855   /* input buffer */
1856   unsigned char buffer[64];
1857 } MD5_CTX;
1858 
1859 static void MD5_Init(MD5_CTX *);
1860 static void MD5_Update(MD5_CTX *, unsigned char *, size_t);
1861 static void MD5_Final(unsigned char *, MD5_CTX *);
1862 
1863 /* Note: these MD5 routines are taken from RFC 1321 */
1864 
1865 #ifdef HAVE_MEMCPY
1866 # define MD5_memcpy(a, b, c) memcpy((a), (b), (c))
1867 # define MD5_memset(a, b, c) memset((a), (b), (c))
1868 #endif
1869 
1870 /* Constants for MD5Transform routine.
1871  */
1872 
1873 #define S11 7
1874 #define S12 12
1875 #define S13 17
1876 #define S14 22
1877 #define S21 5
1878 #define S22 9
1879 #define S23 14
1880 #define S24 20
1881 #define S31 4
1882 #define S32 11
1883 #define S33 16
1884 #define S34 23
1885 #define S41 6
1886 #define S42 10
1887 #define S43 15
1888 #define S44 21
1889 
1890 static void MD5Transform(uint32_t *, unsigned char[64]);
1891 static void Encode(unsigned char *, uint32_t *, unsigned int);
1892 static void Decode(uint32_t *, unsigned char *, unsigned int);
1893 
1894 #ifndef HAVE_MEMCPY
1895 static void MD5_memcpy(unsigned char *, unsigned char *, unsigned int);
1896 static void MD5_memset(unsigned char *, int, unsigned int);
1897 #endif
1898 
1899 static unsigned char PADDING[64] = {
1900   0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1901   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1902   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1903 };
1904 
1905 /* F, G, H and I are basic MD5 functions.
1906  */
1907 #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
1908 #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
1909 #define H(x, y, z) ((x) ^ (y) ^ (z))
1910 #define I(x, y, z) ((y) ^ ((x) | (~z)))
1911 
1912 /* ROTATE_LEFT rotates x left n bits.
1913  */
1914 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
1915 
1916 /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
1917  * Rotation is separate from addition to prevent recomputation.
1918  */
1919 #define FF(a, b, c, d, x, s, ac) { \
1920  (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
1921  (a) = ROTATE_LEFT ((a), (s)); \
1922  (a) += (b); \
1923   }
1924 #define GG(a, b, c, d, x, s, ac) { \
1925  (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
1926  (a) = ROTATE_LEFT ((a), (s)); \
1927  (a) += (b); \
1928   }
1929 #define HH(a, b, c, d, x, s, ac) { \
1930  (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
1931  (a) = ROTATE_LEFT ((a), (s)); \
1932  (a) += (b); \
1933   }
1934 #define II(a, b, c, d, x, s, ac) { \
1935  (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
1936  (a) = ROTATE_LEFT ((a), (s)); \
1937  (a) += (b); \
1938   }
1939 
1940 /* MD5 initialization. Begins an MD5 operation, writing a new context.
1941  */
MD5_Init(MD5_CTX * context)1942 static void MD5_Init(MD5_CTX *context) {
1943   context->count[0] = context->count[1] = 0;
1944 
1945   /* Load magic initialization constants.
1946    */
1947   context->state[0] = 0x67452301;
1948   context->state[1] = 0xefcdab89;
1949   context->state[2] = 0x98badcfe;
1950   context->state[3] = 0x10325476;
1951 }
1952 
1953 /* MD5 block update operation. Continues an MD5 message-digest
1954  * operation, processing another message block, and updating the
1955  * context.
1956  */
MD5_Update(MD5_CTX * context,unsigned char * input,size_t inputLen)1957 static void MD5_Update(MD5_CTX *context, unsigned char *input,
1958     size_t inputLen) {
1959   unsigned int i, index, partLen;
1960 
1961   /* Compute number of bytes mod 64 */
1962   index = (unsigned int)((context->count[0] >> 3) & 0x3F);
1963 
1964   /* Update number of bits */
1965   if ((context->count[0] += ((uint32_t)inputLen << 3))
1966        < ((uint32_t)inputLen << 3))
1967     context->count[1]++;
1968   context->count[1] += ((uint32_t)inputLen >> 29);
1969 
1970   partLen = 64 - index;
1971 
1972   /* Transform as many times as possible */
1973   if (inputLen >= partLen) {
1974     MD5_memcpy((unsigned char *) &context->buffer[index],
1975       (unsigned char *) input, partLen);
1976     MD5Transform(context->state, context->buffer);
1977 
1978     for (i = partLen; i + 63 < inputLen; i += 64)
1979       MD5Transform(context->state, &input[i]);
1980 
1981     index = 0;
1982 
1983   } else
1984     i = 0;
1985 
1986   /* Buffer remaining input */
1987   MD5_memcpy((unsigned char *) &context->buffer[index],
1988     (unsigned char *) &input[i], inputLen-i);
1989 }
1990 
1991 /* MD5 finalization. Ends an MD5 message-digest operation, writing the
1992  * the message digest and zeroizing the context.
1993  */
MD5_Final(unsigned char digest[16],MD5_CTX * context)1994 static void MD5_Final(unsigned char digest[16], MD5_CTX *context) {
1995   unsigned char bits[8];
1996   unsigned int index;
1997   size_t padLen;
1998 
1999   /* Save number of bits */
2000   Encode (bits, context->count, 8);
2001 
2002   /* Pad out to 56 mod 64.
2003    */
2004   index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
2005   padLen = (index < 56) ? (56 - index) : (120 - index);
2006   MD5_Update(context, PADDING, padLen);
2007 
2008   /* Append length (before padding) */
2009   MD5_Update(context, bits, 8);
2010 
2011   /* Store state in digest */
2012   Encode(digest, context->state, 16);
2013 
2014   /* Zeroize sensitive information.
2015    */
2016   MD5_memset((unsigned char *) context, 0, sizeof(*context));
2017 }
2018 
2019 /* MD5 basic transformation. Transforms state based on block.
2020  */
MD5Transform(uint32_t state[4],unsigned char block[64])2021 static void MD5Transform(uint32_t state[4], unsigned char block[64]) {
2022   uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
2023 
2024   Decode(x, block, 64);
2025 
2026   /* Round 1 */
2027   FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
2028   FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
2029   FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
2030   FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
2031   FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
2032   FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
2033   FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
2034   FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
2035   FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
2036   FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
2037   FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
2038   FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
2039   FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
2040   FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
2041   FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
2042   FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
2043 
2044  /* Round 2 */
2045   GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
2046   GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
2047   GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
2048   GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
2049   GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
2050   GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
2051   GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
2052   GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
2053   GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
2054   GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
2055   GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
2056 
2057   GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
2058   GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
2059   GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
2060   GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
2061   GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
2062 
2063   /* Round 3 */
2064   HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
2065   HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
2066   HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
2067   HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
2068   HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
2069   HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
2070   HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
2071   HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
2072   HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
2073   HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
2074   HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
2075   HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
2076   HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
2077   HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
2078   HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
2079   HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
2080 
2081   /* Round 4 */
2082   II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
2083   II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
2084   II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
2085   II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
2086   II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
2087   II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
2088   II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
2089   II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
2090   II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
2091   II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
2092   II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
2093   II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
2094   II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
2095   II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
2096   II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
2097   II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
2098 
2099   state[0] += a;
2100   state[1] += b;
2101   state[2] += c;
2102   state[3] += d;
2103 
2104   /* Zeroize sensitive information.
2105    */
2106   MD5_memset((unsigned char *) x, 0, sizeof(x));
2107 }
2108 
2109 /* Encodes input (unsigned long) into output (unsigned char). Assumes len is
2110  * a multiple of 4.
2111  */
Encode(unsigned char * output,uint32_t * input,unsigned int len)2112 static void Encode(unsigned char *output, uint32_t *input, unsigned int len) {
2113   unsigned int i, j;
2114 
2115   for (i = 0, j = 0; j < len; i++, j += 4) {
2116     output[j] = (unsigned char)(input[i] & 0xff);
2117     output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
2118     output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
2119     output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
2120   }
2121 }
2122 
2123 /* Decodes input (unsigned char) into output (unsigned long). Assumes len is
2124  * a multiple of 4.
2125  */
Decode(uint32_t * output,unsigned char * input,unsigned int len)2126 static void Decode(uint32_t *output, unsigned char *input, unsigned int len) {
2127   unsigned int i, j;
2128 
2129   for (i = 0, j = 0; j < len; i++, j += 4)
2130     output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) |
2131     (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24);
2132 }
2133 
2134 #ifndef HAVE_MEMCPY
2135 /* Note: Replace "for loop" with standard memcpy if possible. */
MD5_memcpy(unsigned char * output,unsigned char * input,unsigned int len)2136 static void MD5_memcpy(unsigned char *output, unsigned char *input,
2137     unsigned int len) {
2138   unsigned int i;
2139 
2140   for (i = 0; i < len; i++) {
2141     output[i] = input[i];
2142   }
2143 }
2144 
2145 /* Note: Replace "for loop" with standard memset if possible. */
MD5_memset(unsigned char * output,int value,unsigned int len)2146 static void MD5_memset(unsigned char *output, int value, unsigned int len) {
2147   unsigned int i;
2148 
2149   for (i = 0; i < len; i++) {
2150     ((char *) output)[i] = (char) value;
2151   }
2152 }
2153 #endif
2154 #endif /* !PR_USE_OPENSSL */
2155 
radius_openlog(void)2156 static int radius_openlog(void) {
2157   int res = 0, xerrno = 0;
2158   config_rec *c;
2159   const char *path;
2160 
2161   c = find_config(main_server->conf, CONF_PARAM, "RadiusLog", FALSE);
2162   if (c == NULL) {
2163     return 0;
2164   }
2165 
2166   path = c->argv[0];
2167   if (strcasecmp(path, "none") == 0) {
2168     return 0;
2169   }
2170 
2171   pr_signals_block();
2172   PRIVS_ROOT
2173   res = pr_log_openfile(path, &radius_logfd, PR_LOG_SYSTEM_MODE);
2174   xerrno = errno;
2175   PRIVS_RELINQUISH
2176   pr_signals_unblock();
2177 
2178   errno = xerrno;
2179   return res;
2180 }
2181 
2182 /* RADIUS routines */
2183 
2184 /* Add an attribute to a RADIUS packet.  Returns the added attribute. */
radius_add_attrib(radius_packet_t * packet,unsigned char type,const unsigned char * data,size_t datalen)2185 static radius_attrib_t *radius_add_attrib(radius_packet_t *packet,
2186     unsigned char type, const unsigned char *data, size_t datalen) {
2187   radius_attrib_t *attrib = NULL;
2188 
2189   attrib = (radius_attrib_t *) ((unsigned char *) packet +
2190     ntohs(packet->length));
2191   attrib->type = type;
2192 
2193   /* Total size of the attribute.  The "+ 2" takes into account the size
2194    * of the attribute identifier.
2195    */
2196   attrib->length = datalen + 2;
2197 
2198   /* Increment the size of the given packet. */
2199   packet->length = htons(ntohs(packet->length) + attrib->length);
2200 
2201   memcpy(attrib->data, data, datalen);
2202 
2203   return attrib;
2204 }
2205 
2206 /* Add a RADIUS message authenticator attribute to the packet. */
radius_set_auth_mac(radius_packet_t * pkt,const unsigned char * secret,size_t secret_len)2207 static void radius_set_auth_mac(radius_packet_t *pkt,
2208    const unsigned char *secret, size_t secret_len) {
2209 #ifdef PR_USE_OPENSSL
2210   const EVP_MD *md;
2211   unsigned char digest[EVP_MAX_MD_SIZE];
2212   unsigned int digest_len = 0, mac_len = 16;
2213   radius_attrib_t *attrib = NULL;
2214 
2215   /* First, add the Message-Authenticator attribute, with a value of all zeroes,
2216    * per RFC 3579, Section 3.2.
2217    */
2218   memset(digest, '\0', sizeof(digest));
2219   attrib = radius_add_attrib(pkt, RADIUS_MESSAGE_AUTHENTICATOR,
2220     (const unsigned char *) digest, mac_len);
2221 
2222   /* Now, calculate the HMAC-MD5 of the packet. */
2223 
2224   md = EVP_md5();
2225   if (HMAC(md, secret, secret_len, (unsigned char *) pkt, ntohs(pkt->length),
2226       digest, &digest_len) == NULL) {
2227     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2228       "error generating Message-Authenticator: %s",
2229       ERR_error_string(ERR_get_error(), NULL));
2230 
2231   } else {
2232     /* Finally, overwrite the all-zeroes Message-Authenticator value with our
2233      * calculated value.
2234      */
2235     memcpy(attrib->data, digest, mac_len);
2236   }
2237 #endif /* PR_USE_OPENSSL */
2238 }
2239 
radius_verify_auth_mac(radius_packet_t * pkt,const char * pkt_type,const unsigned char * secret,size_t secret_len)2240 static int radius_verify_auth_mac(radius_packet_t *pkt, const char *pkt_type,
2241     const unsigned char *secret, size_t secret_len) {
2242   int res = 0;
2243   radius_attrib_t *attrib = NULL;
2244 
2245   /* Handle any Message-Authenticator attribute, per RFC 2869, Section 5.14. */
2246   attrib = radius_get_attrib(pkt, RADIUS_MESSAGE_AUTHENTICATOR);
2247   if (attrib != NULL) {
2248     unsigned char attrib_len;
2249     unsigned int expected_len = 16;
2250 
2251     attrib_len = RADIUS_ATTRIB_LEN(attrib);
2252     if (attrib_len != expected_len) {
2253 #ifdef PR_USE_OPENSSL
2254       const EVP_MD *md;
2255       unsigned char digest[EVP_MAX_MD_SIZE], replied[EVP_MAX_MD_SIZE];
2256       unsigned int digest_len = 0;
2257 
2258       /* First, make a copy of the packet's Message-Authenticator value, for
2259        * comparison with what we will calculate.
2260        */
2261       memset(replied, '\0', sizeof(replied));
2262       memcpy(replied, attrib->data, attrib_len);
2263 
2264       /* Next, zero out the value so that we can calculate it ourselves. */
2265       memset(attrib->data, '\0', attrib_len);
2266 
2267       memset(digest, '\0', sizeof(digest));
2268       md = EVP_md5();
2269       if (HMAC(md, secret, secret_len, (unsigned char *) pkt,
2270           ntohs(pkt->length), digest, &digest_len) == NULL) {
2271         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2272           "error generating Message-Authenticator: %s",
2273           ERR_error_string(ERR_get_error(), NULL));
2274         return 0;
2275       }
2276 
2277       if (memcmp(replied, digest, expected_len) != 0) {
2278         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2279           "packet Message-Authenticator verification failed: mismatched MACs");
2280         errno = EINVAL;
2281         return -1;
2282       }
2283 
2284       res = 0;
2285 
2286 #endif /* PR_USE_OPENSSL */
2287     } else {
2288       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2289         "%s packet has incorrect Message-Authenticator attribute length "
2290         "(%u != %u), rejecting", pkt_type, attrib_len, expected_len);
2291       errno = EINVAL;
2292       return -1;
2293     }
2294 
2295   } else {
2296     pr_trace_msg(trace_channel, 6,
2297       "%s packet lacks Message-Authenticator attribute (%d)", pkt_type,
2298       RADIUS_MESSAGE_AUTHENTICATOR);
2299 
2300     if (radius_opts & RADIUS_OPT_REQUIRE_MAC) {
2301       errno = EPERM;
2302       return -1;
2303     }
2304   }
2305 
2306   return res;
2307 }
2308 
2309 /* Add a RADIUS password attribute to the packet. */
radius_add_passwd(radius_packet_t * packet,unsigned char type,const unsigned char * passwd,unsigned char * secret,size_t secret_len)2310 static void radius_add_passwd(radius_packet_t *packet, unsigned char type,
2311     const unsigned char *passwd, unsigned char *secret, size_t secret_len) {
2312   MD5_CTX ctx, secret_ctx;
2313   radius_attrib_t *attrib = NULL;
2314   unsigned char calculated[RADIUS_VECTOR_LEN];
2315   unsigned char pwhash[PR_TUNABLE_BUFFER_SIZE];
2316   unsigned char *digest = NULL;
2317   register unsigned int i = 0;
2318   size_t pwlen;
2319 
2320   pwlen = strlen((const char *) passwd);
2321 
2322   /* Clear the buffers. */
2323   memset(pwhash, '\0', sizeof(pwhash));
2324 
2325   if (pwlen == 0) {
2326     pwlen = RADIUS_PASSWD_LEN;
2327 
2328   } else if ((pwlen & (RADIUS_PASSWD_LEN - 1)) != 0) {
2329     /* pwlen is not a multiple of RADIUS_PASSWD_LEN, need to prepare a proper
2330      * buffer.
2331      */
2332     memcpy(pwhash, passwd, pwlen);
2333 
2334     /* Round up the length. */
2335     pwlen += (RADIUS_PASSWD_LEN - 1);
2336 
2337     /* Truncate the length, as necessary. */
2338     pwlen &= ~(RADIUS_PASSWD_LEN - 1);
2339 
2340   } else {
2341     /* pwlen is a multiple of RADIUS_PASSWD_LEN, we can just use it. */
2342     memcpy(pwhash, passwd, pwlen);
2343   }
2344 
2345   /* Find the password attribute. */
2346   attrib = radius_get_attrib(packet, RADIUS_PASSWORD);
2347 
2348   if (type == RADIUS_PASSWORD) {
2349     digest = packet->digest;
2350 
2351   } else {
2352     digest = attrib->data;
2353   }
2354 
2355   /* Encrypt the password.  Password: c[0] = p[0] ^ MD5(secret + digest) */
2356   MD5_Init(&secret_ctx);
2357   MD5_Update(&secret_ctx, secret, secret_len);
2358 
2359   /* Save this hash for later. */
2360   ctx = secret_ctx;
2361 
2362   MD5_Update(&ctx, digest, RADIUS_VECTOR_LEN);
2363 
2364   /* Set the calculated digest. */
2365   MD5_Final(calculated, &ctx);
2366 
2367   /* XOR the results. */
2368   radius_xor(pwhash, calculated, RADIUS_PASSWD_LEN);
2369 
2370   /* For each step through: e[i] = p[i] ^ MD5(secret + e[i-1]) */
2371   for (i = 1; i < (pwlen >> 4); i++) {
2372 
2373     /* Start with the old value of the MD5 sum. */
2374     ctx = secret_ctx;
2375 
2376     MD5_Update(&ctx, &pwhash[(i-1) * RADIUS_PASSWD_LEN], RADIUS_PASSWD_LEN);
2377 
2378     /* Set the calculated digest. */
2379     MD5_Final(calculated, &ctx);
2380 
2381     /* XOR the results. */
2382     radius_xor(&pwhash[i * RADIUS_PASSWD_LEN], calculated, RADIUS_PASSWD_LEN);
2383   }
2384 
2385   if (type == RADIUS_OLD_PASSWORD) {
2386     attrib = radius_get_attrib(packet, RADIUS_OLD_PASSWORD);
2387   }
2388 
2389   if (attrib == NULL) {
2390     radius_add_attrib(packet, type, pwhash, pwlen);
2391 
2392   } else {
2393     /* Overwrite the packet data. */
2394     memcpy(attrib->data, pwhash, pwlen);
2395   }
2396 
2397   pr_memscrub(pwhash, sizeof(pwhash));
2398 }
2399 
radius_set_acct_digest(radius_packet_t * packet,const unsigned char * secret,size_t secret_len)2400 static void radius_set_acct_digest(radius_packet_t *packet,
2401     const unsigned char *secret, size_t secret_len) {
2402   MD5_CTX ctx;
2403 
2404   /* Clear the current digest (not needed yet for accounting packets) */
2405   memset(packet->digest, 0, RADIUS_VECTOR_LEN);
2406 
2407   MD5_Init(&ctx);
2408 
2409   /* Add the packet data to the mix. */
2410   MD5_Update(&ctx, (unsigned char *) packet, ntohs(packet->length));
2411 
2412   /* Add the secret to the mix. */
2413   MD5_Update(&ctx, secret, secret_len);
2414 
2415   /* Set the calculated digest in place in the packet. */
2416   MD5_Final(packet->digest, &ctx);
2417 }
2418 
2419 /* Obtain a random digest. */
radius_get_rnd_digest(radius_packet_t * packet)2420 static void radius_get_rnd_digest(radius_packet_t *packet) {
2421   MD5_CTX ctx;
2422   struct timeval tv;
2423   struct timezone tz;
2424 
2425   /* Use the time of day with the best resolution the system can give us,
2426    * often close to microsecond accuracy.
2427    */
2428   gettimeofday(&tv, &tz);
2429 
2430   /* Add in some (possibly) hard to guess information. */
2431   tv.tv_sec ^= (long) (getpid() * getppid());
2432 
2433   /* Use MD5 to obtain (hopefully) cryptographically strong pseudo-random
2434    * numbers
2435    */
2436   MD5_Init(&ctx);
2437   MD5_Update(&ctx, (unsigned char *) &tv, sizeof(tv));
2438   MD5_Update(&ctx, (unsigned char *) &tz, sizeof(tz));
2439 
2440   /* Set the calculated digest in the space provided. */
2441   MD5_Final(packet->digest, &ctx);
2442 }
2443 
2444 /* RADIUS packet manipulation functions.
2445  */
2446 
2447 /* For iterating through all of the attributes in a packet, callers can
2448  * provide a pointer to the previous attribute returned, or NULL.
2449  */
radius_get_next_attrib(radius_packet_t * packet,unsigned char attrib_type,unsigned int * packet_len,radius_attrib_t * prev_attrib)2450 static radius_attrib_t *radius_get_next_attrib(radius_packet_t *packet,
2451     unsigned char attrib_type, unsigned int *packet_len,
2452     radius_attrib_t *prev_attrib) {
2453   radius_attrib_t *attrib = NULL;
2454   unsigned int len;
2455 
2456   if (packet_len == NULL) {
2457     len = ntohs(packet->length) - RADIUS_HEADER_LEN;
2458 
2459   } else {
2460     len = *packet_len;
2461   }
2462 
2463   if (prev_attrib == NULL) {
2464     attrib = (radius_attrib_t *) &packet->data;
2465 
2466   } else {
2467     attrib = prev_attrib;
2468   }
2469 
2470   while (attrib->type != attrib_type) {
2471     if (attrib->length == 0 ||
2472         (len -= attrib->length) <= 0) {
2473 
2474       /* Requested attribute not found. */
2475       if (packet_len != NULL) {
2476         *packet_len = 0;
2477       }
2478 
2479       return NULL;
2480     }
2481 
2482     /* Examine the next attribute in the packet. */
2483     attrib = (radius_attrib_t *) ((char *) attrib + attrib->length);
2484   }
2485 
2486   if (packet_len != NULL) {
2487     *packet_len = len;
2488   }
2489 
2490   return attrib;
2491 }
2492 
radius_get_attrib(radius_packet_t * packet,unsigned char attrib_type)2493 static radius_attrib_t *radius_get_attrib(radius_packet_t *packet,
2494     unsigned char attrib_type) {
2495   return radius_get_next_attrib(packet, attrib_type, NULL, NULL);
2496 }
2497 
2498 /* Find a Vendor-Specific Attribute (VSA) in a RADIUS packet.  Note that
2499  * the packet length is always kept in network byte order.
2500  */
radius_get_vendor_attrib(radius_packet_t * packet,unsigned char type)2501 static radius_attrib_t *radius_get_vendor_attrib(radius_packet_t *packet,
2502     unsigned char type) {
2503   radius_attrib_t *attrib = (radius_attrib_t *) &packet->data;
2504   int len = ntohs(packet->length) - RADIUS_HEADER_LEN;
2505 
2506   while (attrib) {
2507     unsigned int vendor_id = 0;
2508 
2509     pr_signals_handle();
2510 
2511     if (attrib->length == 0) {
2512       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2513         "packet includes invalid length (%u) for attribute type %u, rejecting",
2514         attrib->length, attrib->type);
2515       return NULL;
2516     }
2517 
2518     if (attrib->type != RADIUS_VENDOR_SPECIFIC) {
2519       len -= attrib->length;
2520       attrib = (radius_attrib_t *) ((char *) attrib + attrib->length);
2521       continue;
2522     }
2523 
2524     /* The first four octets (bytes) of data will contain the Vendor-Id. */
2525     if (attrib->length >= 4) {
2526       memcpy(&vendor_id, attrib->data, 4);
2527       vendor_id = ntohl(vendor_id);
2528     }
2529 
2530     if (vendor_id != radius_vendor_id) {
2531       len -= attrib->length;
2532       attrib = (radius_attrib_t *) ((char *) attrib + attrib->length);
2533       continue;
2534     }
2535 
2536     /* Parse the data value for this attribute into a VSA structure. */
2537     if (attrib->length > 4) {
2538       radius_attrib_t *vsa = NULL;
2539 
2540       vsa = (radius_attrib_t *) ((char *) attrib->data + sizeof(int));
2541 
2542       /* Does this VSA have the type requested? */
2543       if (vsa->type != type) {
2544         len -= attrib->length;
2545         attrib = (radius_attrib_t *) ((char *) attrib + attrib->length);
2546         continue;
2547       }
2548 
2549       return vsa;
2550     }
2551   }
2552 
2553   return NULL;
2554 }
2555 
2556 /* Build a RADIUS packet, initializing some of the header and adding
2557  * common attributes.
2558  */
radius_build_packet(radius_packet_t * packet,const unsigned char * user,const unsigned char * passwd,unsigned char * secret,size_t secret_len)2559 static void radius_build_packet(radius_packet_t *packet,
2560     const unsigned char *user, const unsigned char *passwd,
2561     unsigned char *secret, size_t secret_len) {
2562   unsigned int nas_port_type = htonl(RADIUS_NAS_PORT_TYPE_VIRTUAL);
2563   int nas_port = htonl(main_server->ServerPort);
2564   char *caller_id = NULL;
2565   const char *nas_identifier = NULL;
2566   size_t userlen;
2567 
2568   /* Set the packet length. */
2569   packet->length = htons(RADIUS_HEADER_LEN);
2570 
2571   /* Obtain a random digest. */
2572   radius_get_rnd_digest(packet);
2573 
2574   /* Set the ID for the packet. */
2575   packet->id = packet->digest[0];
2576 
2577   /* Add the user attribute. */
2578   userlen = strlen((const char *) user);
2579   radius_add_attrib(packet, RADIUS_USER_NAME, user, userlen);
2580 
2581   /* Add the password attribute, if given. */
2582   if (passwd) {
2583     radius_add_passwd(packet, RADIUS_PASSWORD, passwd, secret, secret_len);
2584 
2585   } else if (packet->code != RADIUS_ACCT_REQUEST) {
2586     /* Add a NULL password if necessary. */
2587     radius_add_passwd(packet, RADIUS_PASSWORD, (const unsigned char *) "",
2588       secret, 1);
2589   }
2590 
2591   /* Add a NAS identifier attribute of the service name, e.g. 'ftp'. */
2592 
2593   nas_identifier = pr_session_get_protocol(0);
2594   if (radius_nas_identifier_config != NULL) {
2595     nas_identifier = radius_nas_identifier_config;
2596   }
2597 
2598   radius_add_attrib(packet, RADIUS_NAS_IDENTIFIER,
2599     (const unsigned char *) nas_identifier,
2600     strlen((const char *) nas_identifier));
2601 
2602 #ifdef PR_USE_IPV6
2603   if (pr_netaddr_use_ipv6()) {
2604     const pr_netaddr_t *local_addr;
2605     int family;
2606 
2607     local_addr = pr_netaddr_get_sess_local_addr();
2608     family = pr_netaddr_get_family(local_addr);
2609 
2610     switch (family) {
2611       case AF_INET: {
2612         struct in_addr *inaddr;
2613 
2614         inaddr = pr_netaddr_get_inaddr(local_addr);
2615 
2616         /* Add a NAS-IP-Address attribute. */
2617         radius_add_attrib(packet, RADIUS_NAS_IP_ADDRESS,
2618           (unsigned char *) &(inaddr->s_addr), sizeof(inaddr->s_addr));
2619         break;
2620       }
2621 
2622       case AF_INET6: {
2623         if (pr_netaddr_is_v4mappedv6(local_addr)) {
2624           pr_netaddr_t *v4_addr;
2625 
2626           /* Note: in the future, switch to using a per-packet pool. */
2627           v4_addr = pr_netaddr_v6tov4(radius_pool, local_addr);
2628           if (v4_addr != NULL) {
2629             struct in_addr *inaddr;
2630 
2631             inaddr = pr_netaddr_get_inaddr(v4_addr);
2632 
2633             /* Add a NAS-IP-Address attribute. */
2634             radius_add_attrib(packet, RADIUS_NAS_IP_ADDRESS,
2635               (unsigned char *) &(inaddr->s_addr), sizeof(inaddr->s_addr));
2636 
2637           } else {
2638             (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2639               "error converting '%s' to IPv4 address: %s",
2640               pr_netaddr_get_ipstr(local_addr), strerror(errno));
2641           }
2642 
2643         } else {
2644           struct in6_addr *inaddr;
2645           uint32_t ipv6_addr[4];
2646 
2647           inaddr = pr_netaddr_get_inaddr(pr_netaddr_get_sess_local_addr());
2648 
2649           /* Ideally we would use the inaddr->s6_addr32 to get to the 128-bit
2650            * IPv6 address.  But `s6_addr32' turns out to be a macro that is not
2651            * available on all systems (FreeBSD, for example, does not provide
2652            * this macro unless you're building its kernel).
2653            *
2654            * As a workaround, try using the (hopefully) more portable s6_addr
2655            * macro.
2656            */
2657           memcpy(ipv6_addr, inaddr->s6_addr, sizeof(ipv6_addr));
2658 
2659           /* Add a NAS-IPv6-Address attribute. */
2660           radius_add_attrib(packet, RADIUS_NAS_IPV6_ADDRESS,
2661             (unsigned char *) ipv6_addr, sizeof(ipv6_addr));
2662         }
2663 
2664         break;
2665       }
2666     }
2667 
2668   } else {
2669 #else
2670   if (TRUE) {
2671 #endif /* PR_USE_IPV6 */
2672     struct in_addr *inaddr;
2673 
2674     inaddr = pr_netaddr_get_inaddr(pr_netaddr_get_sess_local_addr());
2675 
2676     /* Add a NAS-IP-Address attribute. */
2677     radius_add_attrib(packet, RADIUS_NAS_IP_ADDRESS,
2678       (unsigned char *) &(inaddr->s_addr), sizeof(inaddr->s_addr));
2679   }
2680 
2681   /* Add a NAS port attribute. */
2682   radius_add_attrib(packet, RADIUS_NAS_PORT, (unsigned char *) &nas_port,
2683     sizeof(int));
2684 
2685   /* Add a NAS port type attribute. */
2686   radius_add_attrib(packet, RADIUS_NAS_PORT_TYPE,
2687     (unsigned char *) &nas_port_type, sizeof(int));
2688 
2689   /* Add the calling station ID attribute (this is the IP of the connecting
2690    * client).
2691    */
2692   caller_id = (char *) pr_netaddr_get_ipstr(pr_netaddr_get_sess_remote_addr());
2693 
2694   radius_add_attrib(packet, RADIUS_CALLING_STATION_ID,
2695     (const unsigned char *) caller_id, strlen(caller_id));
2696 }
2697 
2698 static radius_server_t *radius_make_server(pool *parent_pool) {
2699   pool *server_pool = NULL;
2700   radius_server_t *server = NULL;
2701 
2702   /* sanity check */
2703   if (!parent_pool)
2704     return NULL;
2705 
2706   /* allocate a subpool for the new rec */
2707   server_pool = make_sub_pool(parent_pool);
2708 
2709   /* allocate the rec from the subpool */
2710   server = (radius_server_t *) pcalloc(server_pool,
2711     sizeof(radius_server_t));
2712 
2713   /* set the members to sane default values */
2714   server->pool = server_pool;
2715   server->addr = NULL;
2716   server->port = RADIUS_AUTH_PORT;
2717   server->secret = NULL;
2718   server->secret_len = 0;
2719   server->timeout = DEFAULT_RADIUS_TIMEOUT;
2720   server->next = NULL;
2721 
2722   return server;
2723 }
2724 
2725 static int radius_open_socket(void) {
2726   int sockfd = -1;
2727   struct sockaddr_in *radius_sockaddr_in = NULL;
2728   unsigned short local_port = 0;
2729 
2730   /* Obtain a socket descriptor. */
2731   sockfd = socket(AF_INET, SOCK_DGRAM, 0);
2732   if (sockfd < 0) {
2733     int xerrno = errno;
2734 
2735     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2736       "notice: unable to open socket for communication: %s",
2737       strerror(xerrno));
2738 
2739     errno = xerrno;
2740     return -1;
2741   }
2742 
2743   /* Set the members appropriately to bind to the descriptor. */
2744   memset((void *) &radius_local_sock, 0, sizeof(radius_local_sock));
2745   radius_sockaddr_in = (struct sockaddr_in *) &radius_local_sock;
2746   radius_sockaddr_in->sin_family = AF_INET;
2747   radius_sockaddr_in->sin_addr.s_addr = INADDR_ANY;
2748 
2749   /*  Use our process ID as a local port for RADIUS.
2750    */
2751   local_port = (getpid() & 0x7fff) + 1024;
2752   do {
2753     pr_signals_handle();
2754 
2755     local_port++;
2756     radius_sockaddr_in->sin_port = htons(local_port);
2757 
2758   } while ((bind(sockfd, &radius_local_sock, sizeof(radius_local_sock)) < 0) &&
2759     (local_port < USHRT_MAX));
2760 
2761   if (local_port >= USHRT_MAX) {
2762     (void) close(sockfd);
2763     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2764       "notice: unable to bind to socket: no open local ports");
2765     errno = EPERM;
2766     return -1;
2767   }
2768 
2769   /* Done */
2770   return sockfd;
2771 }
2772 
2773 static radius_packet_t *radius_recv_packet(int sockfd, unsigned int timeout) {
2774   static unsigned char recvbuf[RADIUS_PACKET_LEN];
2775   radius_packet_t *packet = NULL;
2776   int res = 0, recvlen = -1;
2777   socklen_t sockaddrlen = sizeof(struct sockaddr);
2778   struct timeval tv;
2779   fd_set rset;
2780 
2781   /* receive the response, waiting as necessary */
2782   memset(recvbuf, '\0', sizeof(recvbuf));
2783 
2784   tv.tv_sec = timeout;
2785   tv.tv_usec = 0;
2786 
2787   FD_ZERO(&rset);
2788   FD_SET(sockfd, &rset);
2789 
2790   res = select(sockfd + 1, &rset, NULL, NULL, &tv);
2791   if (res == 0) {
2792     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2793       "server failed to respond in %u seconds", timeout);
2794     return NULL;
2795 
2796   } else if (res < 0) {
2797     int xerrno = errno;
2798 
2799     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2800       "error: unable to receive response: %s", strerror(xerrno));
2801 
2802     errno = xerrno;
2803     return NULL;
2804   }
2805 
2806   recvlen = recvfrom(sockfd, (char *) recvbuf, RADIUS_PACKET_LEN, 0,
2807     &radius_remote_sock, &sockaddrlen);
2808   if (recvlen < 0) {
2809     int xerrno = errno;
2810 
2811     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2812       "error reading packet: %s", strerror(xerrno));
2813 
2814     errno = xerrno;
2815     return NULL;
2816   }
2817 
2818   packet = (radius_packet_t *) recvbuf;
2819 
2820   /* Make sure the packet is of valid length. */
2821   if (ntohs(packet->length) != recvlen ||
2822       ntohs(packet->length) > RADIUS_PACKET_LEN) {
2823     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2824       "received corrupted packet");
2825     return NULL;
2826   }
2827 
2828   return packet;
2829 }
2830 
2831 static int radius_send_packet(int sockfd, radius_packet_t *packet,
2832     radius_server_t *server) {
2833   int res;
2834   struct sockaddr_in *radius_sockaddr_in =
2835     (struct sockaddr_in *) &radius_remote_sock;
2836 
2837   /* Set the members appropriately to send to the descriptor. */
2838   memset((void *) &radius_remote_sock, '\0', sizeof(radius_remote_sock));
2839   radius_sockaddr_in->sin_family = AF_INET;
2840   radius_sockaddr_in->sin_addr.s_addr = pr_netaddr_get_addrno(server->addr);
2841   radius_sockaddr_in->sin_port = htons(server->port);
2842 
2843   res = sendto(sockfd, (char *) packet, ntohs(packet->length), 0,
2844     &radius_remote_sock, sizeof(struct sockaddr_in));
2845   if (res < 0) {
2846     int xerrno = errno;
2847 
2848     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2849       "error: unable to send packet: %s", strerror(xerrno));
2850 
2851     errno = xerrno;
2852     return -1;
2853   }
2854 
2855   (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2856     "sending packet to %s:%u", inet_ntoa(radius_sockaddr_in->sin_addr),
2857     ntohs(radius_sockaddr_in->sin_port));
2858 
2859   return 0;
2860 }
2861 
2862 static int radius_start_accting(void) {
2863   int sockfd = -1, acct_status = 0, acct_authentic = 0, now = 0, pid_len = 0;
2864   radius_packet_t *request = NULL, *response = NULL;
2865   radius_server_t *acct_server = NULL;
2866   unsigned char recvd_response = FALSE, *authenticated = NULL;
2867   char pid_str[16];
2868 
2869   /* Check to see if RADIUS accounting should be done. */
2870   if (radius_engine == FALSE ||
2871       radius_acct_server == NULL) {
2872     return 0;
2873   }
2874 
2875   /* Only do accounting for authenticated users. */
2876   authenticated = get_param_ptr(main_server->conf, "authenticated", FALSE);
2877   if (authenticated == NULL ||
2878       *authenticated == FALSE) {
2879     return 0;
2880   }
2881 
2882   /* Open a RADIUS socket */
2883   sockfd = radius_open_socket();
2884   if (sockfd < 0) {
2885     int xerrno = errno;
2886 
2887     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2888       "socket open failed: %s", strerror(xerrno));
2889 
2890     errno = xerrno;
2891     return -1;
2892   }
2893 
2894   /* Allocate a packet. */
2895   request = (radius_packet_t *) pcalloc(radius_pool, sizeof(radius_packet_t));
2896 
2897   now = htonl(time(NULL));
2898 
2899   memset(pid_str, '\0', sizeof(pid_str));
2900   pid_len = pr_snprintf(pid_str, sizeof(pid_str), "%08u",
2901     (unsigned int) session.pid);
2902 
2903   /* Loop through the list of servers, trying each one until the packet is
2904    * successfully sent.
2905    */
2906   acct_server = radius_acct_server;
2907 
2908   while (acct_server) {
2909     pr_signals_handle();
2910 
2911     /* Clear the packet. */
2912     memset(request, '\0', sizeof(radius_packet_t));
2913 
2914     /* Build the packet. */
2915     request->code = RADIUS_ACCT_REQUEST;
2916     radius_build_packet(request,
2917       radius_realm ?
2918         (const unsigned char *) pstrcat(radius_pool, session.user,
2919           radius_realm, NULL) :
2920         (const unsigned char *) session.user, NULL, acct_server->secret,
2921         acct_server->secret_len);
2922 
2923     radius_last_acct_pkt_id = request->id;
2924 
2925     /* Add accounting attributes. */
2926     acct_status = htonl(RADIUS_ACCT_STATUS_START);
2927     radius_add_attrib(request, RADIUS_ACCT_STATUS_TYPE,
2928       (unsigned char *) &acct_status, sizeof(int));
2929 
2930     radius_add_attrib(request, RADIUS_ACCT_SESSION_ID,
2931       (const unsigned char *) pid_str, pid_len);
2932 
2933     acct_authentic = htonl(RADIUS_AUTH_LOCAL);
2934     radius_add_attrib(request, RADIUS_ACCT_AUTHENTIC,
2935       (unsigned char *) &acct_authentic, sizeof(int));
2936 
2937     radius_add_attrib(request, RADIUS_ACCT_EVENT_TS, (unsigned char *) &now,
2938       sizeof(int));
2939 
2940     if (radius_acct_user != NULL) {
2941       /* See RFC 2865, Section 5.1. */
2942       radius_add_attrib(request, RADIUS_USER_NAME,
2943         (const unsigned char *) radius_acct_user, radius_acct_userlen);
2944     }
2945 
2946     if (radius_acct_class != NULL) {
2947       radius_add_attrib(request, RADIUS_CLASS,
2948         (const unsigned char *) radius_acct_class, radius_acct_classlen);
2949     }
2950 
2951     /* Calculate the signature. */
2952     radius_set_acct_digest(request, acct_server->secret,
2953       acct_server->secret_len);
2954 
2955     /* Send the request. */
2956     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2957       "sending start acct request packet");
2958     if (radius_send_packet(sockfd, request, acct_server) < 0) {
2959       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2960         "packet send failed");
2961       acct_server = acct_server->next;
2962       continue;
2963     }
2964 
2965     /* Receive the response. */
2966     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2967       "receiving acct response packet");
2968     response = radius_recv_packet(sockfd, acct_server->timeout);
2969     if (response == NULL) {
2970       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2971         "packet receive failed");
2972       acct_server = acct_server->next;
2973       continue;
2974     }
2975 
2976     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2977       "packet receive succeeded");
2978     recvd_response = TRUE;
2979     break;
2980   }
2981 
2982   /* Close the socket. */
2983   (void) close(sockfd);
2984 
2985   if (recvd_response) {
2986 
2987     /* Verify the response. */
2988     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2989       "verifying packet");
2990     if (radius_verify_packet(request, response, acct_server->secret,
2991         acct_server->secret_len) < 0) {
2992       return -1;
2993     }
2994 
2995     /* Handle the response. */
2996     switch (response->code) {
2997       case RADIUS_ACCT_RESPONSE:
2998         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
2999           "accounting started for user '%s'", session.user);
3000         return 0;
3001 
3002       default:
3003         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3004           "notice: server returned unknown response code: %02x",
3005           response->code);
3006         return -1;
3007     }
3008 
3009   } else {
3010     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3011       "error: no acct servers responded");
3012   }
3013 
3014   /* Default return value. */
3015   return -1;
3016 }
3017 
3018 /* Maps the ProFTPD disconnect reason code to the RADIUS Acct-Terminate-Cause
3019  * attribute values.
3020  */
3021 static unsigned int radius_get_terminate_cause(void) {
3022   unsigned int cause = RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAIL;
3023 
3024   switch (session.disconnect_reason) {
3025     case PR_SESS_DISCONNECT_CLIENT_QUIT:
3026       cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
3027       break;
3028 
3029     case PR_SESS_DISCONNECT_CLIENT_EOF:
3030       cause = RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE;
3031       break;
3032 
3033     case PR_SESS_DISCONNECT_SIGNAL:
3034       cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET;
3035       break;
3036 
3037     case PR_SESS_DISCONNECT_SERVER_SHUTDOWN:
3038       cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
3039       break;
3040 
3041     case PR_SESS_DISCONNECT_TIMEOUT: {
3042       const char *details = NULL;
3043 
3044       pr_session_get_disconnect_reason(&details);
3045       if (details != NULL) {
3046         if (strcasecmp(details, "TimeoutIdle") == 0) {
3047           cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
3048 
3049         } else if (strcasecmp(details, "TimeoutSession") == 0) {
3050           cause = RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
3051         }
3052       }
3053 
3054       break;
3055     }
3056   }
3057 
3058   return cause;
3059 }
3060 
3061 static int radius_stop_accting(void) {
3062   int sockfd = -1, acct_status = 0, acct_authentic = 0, event_ts = 0,
3063     now = 0, pid_len = 0, session_duration = 0;
3064   unsigned int terminate_cause = 0;
3065   radius_packet_t *request = NULL, *response = NULL;
3066   radius_server_t *acct_server = NULL;
3067   unsigned char recvd_response = FALSE, *authenticated = NULL;
3068   off_t radius_session_bytes_in = 0;
3069   off_t radius_session_bytes_out = 0;
3070   char pid_str[16];
3071 
3072   /* Check to see if RADIUS accounting should be done. */
3073   if (radius_engine == FALSE ||
3074       radius_acct_server == NULL) {
3075     return 0;
3076   }
3077 
3078   /* Only do accounting for authenticated users. */
3079   authenticated = get_param_ptr(main_server->conf, "authenticated", FALSE);
3080   if (authenticated == NULL ||
3081       *authenticated == FALSE) {
3082     return 0;
3083   }
3084 
3085   /* Open a RADIUS socket */
3086   sockfd = radius_open_socket();
3087   if (sockfd < 0) {
3088     int xerrno = errno;
3089 
3090     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3091       "socket open failed: %s", strerror(xerrno));
3092 
3093     errno = xerrno;
3094     return -1;
3095   }
3096 
3097   /* Allocate a packet. */
3098   request = (radius_packet_t *) pcalloc(radius_pool, sizeof(radius_packet_t));
3099 
3100   now = time(NULL);
3101   event_ts = htonl(now);
3102   session_duration = htonl(now - radius_session_start);
3103   terminate_cause = htonl(radius_get_terminate_cause());
3104 
3105   memset(pid_str, '\0', sizeof(pid_str));
3106   pid_len = pr_snprintf(pid_str, sizeof(pid_str)-1, "%08u",
3107     (unsigned int) session.pid);
3108 
3109   /* Loop through the list of servers, trying each one until the packet is
3110    * successfully sent.
3111    */
3112   acct_server = radius_acct_server;
3113 
3114   while (acct_server) {
3115     const char *ip_str;
3116 
3117     pr_signals_handle();
3118 
3119     /* Clear the packet. */
3120     memset(request, '\0', sizeof(radius_packet_t));
3121 
3122     /* Build the packet. */
3123     request->code = RADIUS_ACCT_REQUEST;
3124     radius_build_packet(request,
3125       radius_realm ?
3126         (const unsigned char *) pstrcat(radius_pool, session.user,
3127           radius_realm, NULL) :
3128         (const unsigned char *) session.user, NULL, acct_server->secret,
3129         acct_server->secret_len);
3130 
3131     /* Use the ID of the last accounting packet sent, plus one.  Be sure
3132      * to handle the datatype overflow case.
3133      */
3134     request->id = radius_last_acct_pkt_id + 1;
3135     if (request->id == 0) {
3136       request->id = 1;
3137     }
3138 
3139     /* Add accounting attributes. */
3140     acct_status = htonl(RADIUS_ACCT_STATUS_STOP);
3141     radius_add_attrib(request, RADIUS_ACCT_STATUS_TYPE,
3142       (unsigned char *) &acct_status, sizeof(int));
3143 
3144     radius_add_attrib(request, RADIUS_ACCT_SESSION_ID,
3145       (const unsigned char *) pid_str, pid_len);
3146 
3147     acct_authentic = htonl(RADIUS_AUTH_LOCAL);
3148     radius_add_attrib(request, RADIUS_ACCT_AUTHENTIC,
3149       (unsigned char *) &acct_authentic, sizeof(int));
3150 
3151     radius_add_attrib(request, RADIUS_ACCT_SESSION_TIME,
3152       (unsigned char *) &session_duration, sizeof(int));
3153 
3154     radius_session_bytes_in = htonl(session.total_bytes_in);
3155     radius_add_attrib(request, RADIUS_ACCT_INPUT_OCTETS,
3156       (unsigned char *) &radius_session_bytes_in, sizeof(int));
3157 
3158     radius_session_bytes_out = htonl(session.total_bytes_out);
3159     radius_add_attrib(request, RADIUS_ACCT_OUTPUT_OCTETS,
3160       (unsigned char *) &radius_session_bytes_out, sizeof(int));
3161 
3162     radius_add_attrib(request, RADIUS_ACCT_TERMINATE_CAUSE,
3163       (unsigned char *) &terminate_cause, sizeof(int));
3164 
3165     radius_add_attrib(request, RADIUS_ACCT_EVENT_TS,
3166       (unsigned char *) &event_ts, sizeof(int));
3167 
3168     if (radius_acct_user != NULL) {
3169       /* See RFC 2865, Section 5.1. */
3170       radius_add_attrib(request, RADIUS_USER_NAME,
3171         (const unsigned char *) radius_acct_user, radius_acct_userlen);
3172     }
3173 
3174     if (radius_acct_class != NULL) {
3175       radius_add_attrib(request, RADIUS_CLASS,
3176         (const unsigned char *) radius_acct_class, radius_acct_classlen);
3177     }
3178 
3179     /* Calculate the signature. */
3180     radius_set_acct_digest(request, acct_server->secret,
3181       acct_server->secret_len);
3182 
3183     /* Send the request. */
3184     ip_str = pr_netaddr_get_ipstr(acct_server->addr);
3185     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3186       "sending stop acct request packet to %s#%u", ip_str, acct_server->port);
3187     if (radius_send_packet(sockfd, request, acct_server) < 0) {
3188       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3189         "packet send failed to %s#%u", ip_str, acct_server->port);
3190       acct_server = acct_server->next;
3191       continue;
3192     }
3193 
3194     /* Receive the response. */
3195     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3196       "receiving acct response packet");
3197     response = radius_recv_packet(sockfd, acct_server->timeout);
3198     if (response == NULL) {
3199       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3200         "packet receive failed from %s#%u", ip_str, acct_server->port);
3201       acct_server = acct_server->next;
3202       continue;
3203     }
3204 
3205     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3206       "packet receive succeeded succeeded from %s#%u", ip_str,
3207       acct_server->port);
3208     recvd_response = TRUE;
3209     break;
3210   }
3211 
3212   /* Close the socket. */
3213   (void) close(sockfd);
3214 
3215   if (recvd_response) {
3216 
3217     /* Verify the response. */
3218     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3219       "verifying packet");
3220     if (radius_verify_packet(request, response, acct_server->secret,
3221         acct_server->secret_len) < 0) {
3222       return -1;
3223     }
3224 
3225     /* Handle the response. */
3226     switch (response->code) {
3227       case RADIUS_ACCT_RESPONSE:
3228         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3229           "accounting ended for user '%s'", session.user);
3230         return 0;
3231 
3232       default:
3233         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3234           "notice: server returned unknown response code: %02x",
3235           response->code);
3236         return -1;
3237     }
3238 
3239   } else {
3240     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3241       "error: no accounting servers responded");
3242   }
3243 
3244   /* Default return value. */
3245   return -1;
3246 }
3247 
3248 /* Verify the response packet from the server. */
3249 static int radius_verify_packet(radius_packet_t *req_packet,
3250     radius_packet_t *resp_packet, const unsigned char *secret,
3251     size_t secret_len) {
3252   MD5_CTX ctx;
3253   unsigned char calculated[RADIUS_VECTOR_LEN], replied[RADIUS_VECTOR_LEN];
3254 
3255   /* sanity check */
3256   if (req_packet == NULL ||
3257       resp_packet == NULL ||
3258       secret == NULL) {
3259     errno = EINVAL;
3260     return -1;
3261   }
3262 
3263   /* NOTE: add checks for too-big, too-small packets, invalid packet->length
3264    * values, etc.
3265    */
3266 
3267   /* Check that the packet IDs match. */
3268   if (resp_packet->id != req_packet->id) {
3269     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3270       "packet verification failed: response packet ID %d does not "
3271       "match the request packet ID %d", resp_packet->id, req_packet->id);
3272     return -1;
3273   }
3274 
3275   /* Make sure the buffers are void of junk values. */
3276   memset(calculated, '\0', sizeof(calculated));
3277   memset(replied, '\0', sizeof(replied));
3278 
3279   /* Save a copy of the response's digest. */
3280   memcpy(replied, resp_packet->digest, RADIUS_VECTOR_LEN);
3281 
3282   /* Copy in the digest sent in the request. */
3283   memcpy(resp_packet->digest, req_packet->digest, RADIUS_VECTOR_LEN);
3284 
3285   /* Re-calculate a digest from the given packet, and compare it against
3286    * the provided response digest:
3287    *   MD5(response packet header + digest + response packet data + secret)
3288    */
3289   MD5_Init(&ctx);
3290   MD5_Update(&ctx, (unsigned char *) resp_packet, ntohs(resp_packet->length));
3291 
3292   if (*secret) {
3293     MD5_Update(&ctx, secret, secret_len);
3294   }
3295 
3296   /* Set the calculated digest. */
3297   MD5_Final(calculated, &ctx);
3298 
3299   /* Do the digests match properly? */
3300   if (memcmp(calculated, replied, RADIUS_VECTOR_LEN) != 0) {
3301     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3302       "packet verification failed: mismatched digests");
3303     errno = EINVAL;
3304     return -1;
3305   }
3306 
3307   return 0;
3308 }
3309 
3310 /* Authentication handlers
3311  */
3312 
3313 MODRET radius_auth(cmd_rec *cmd) {
3314 
3315   /* This authentication check has already been performed; I just need
3316    * to report the results of that check now.
3317    */
3318   if (radius_auth_ok) {
3319     session.auth_mech = "mod_radius.c";
3320     return PR_HANDLED(cmd);
3321 
3322   } else if (radius_auth_reject) {
3323     return PR_ERROR_INT(cmd, PR_AUTH_BADPWD);
3324   }
3325 
3326   /* Default return value. */
3327   return PR_DECLINED(cmd);
3328 }
3329 
3330 MODRET radius_check(cmd_rec *cmd) {
3331   return PR_DECLINED(cmd);
3332 }
3333 
3334 MODRET radius_name2uid(cmd_rec *cmd) {
3335   return PR_DECLINED(cmd);
3336 }
3337 
3338 MODRET radius_name2gid(cmd_rec *cmd) {
3339   return PR_DECLINED(cmd);
3340 }
3341 
3342 MODRET radius_uid2name(cmd_rec *cmd) {
3343   return PR_DECLINED(cmd);
3344 }
3345 
3346 MODRET radius_gid2name(cmd_rec *cmd) {
3347   return PR_DECLINED(cmd);
3348 }
3349 
3350 MODRET radius_endgrent(cmd_rec *cmd) {
3351   return PR_DECLINED(cmd);
3352 }
3353 
3354 MODRET radius_getgrnam(cmd_rec *cmd) {
3355   return PR_DECLINED(cmd);
3356 }
3357 
3358 MODRET radius_getgrent(cmd_rec *cmd) {
3359   return PR_DECLINED(cmd);
3360 }
3361 
3362 MODRET radius_getgrgid(cmd_rec *cmd) {
3363   return PR_DECLINED(cmd);
3364 }
3365 
3366 MODRET radius_getgroups(cmd_rec *cmd) {
3367 
3368   if (radius_have_group_info) {
3369     array_header *gids = NULL, *groups = NULL;
3370     register unsigned int i = 0;
3371 
3372     /* Return the faked group information. */
3373 
3374     /* Don't forget to include the primary GID (with accompanying name!)
3375      * in the returned info -- if provided.  Otherwise, well...the user
3376      * is out of luck.
3377      */
3378 
3379     /* Check for NULL values */
3380     if (cmd->argv[1]) {
3381       gids = (array_header *) cmd->argv[1];
3382 
3383       if (radius_have_user_info) {
3384         *((gid_t *) push_array(gids)) = radius_passwd.pw_gid;
3385       }
3386 
3387       for (i = 0; i < radius_addl_group_count; i++) {
3388         *((gid_t *) push_array(gids)) = radius_addl_group_ids[i];
3389       }
3390     }
3391 
3392     if (cmd->argv[2]) {
3393       groups = (array_header *) cmd->argv[2];
3394 
3395       if (radius_have_user_info) {
3396         *((char **) push_array(groups)) = radius_prime_group_name;
3397       }
3398 
3399       for (i = 0; i < radius_addl_group_count; i++) {
3400         *((char **) push_array(groups)) = radius_addl_group_names[i];
3401       }
3402     }
3403 
3404     /* Increment this count, for the sake of proper reporting back to the
3405      * getgroups() caller.
3406      */
3407     if (radius_have_user_info) {
3408       radius_addl_group_count++;
3409     }
3410 
3411     return mod_create_data(cmd, (void *) &radius_addl_group_count);
3412   }
3413 
3414   return PR_DECLINED(cmd);
3415 }
3416 
3417 MODRET radius_setgrent(cmd_rec *cmd) {
3418   return PR_DECLINED(cmd);
3419 }
3420 
3421 MODRET radius_endpwent(cmd_rec *cmd) {
3422   return PR_DECLINED(cmd);
3423 }
3424 
3425 MODRET radius_getpwnam(cmd_rec *cmd) {
3426 
3427   if (radius_have_user_info) {
3428 
3429     if (radius_passwd.pw_name == NULL) {
3430       radius_passwd.pw_name = pstrdup(radius_pool, cmd->argv[0]);
3431     }
3432 
3433     if (strcmp(cmd->argv[0], radius_passwd.pw_name) == 0) {
3434 
3435       /* Return the faked user information. */
3436       return mod_create_data(cmd, &radius_passwd);
3437     }
3438   }
3439 
3440   /* Default response */
3441   return PR_DECLINED(cmd);
3442 }
3443 
3444 MODRET radius_getpwent(cmd_rec *cmd) {
3445 
3446   if (radius_have_user_info) {
3447 
3448     /* Return the faked user information. */
3449     return mod_create_data(cmd, &radius_passwd);
3450   }
3451 
3452   /* Default response */
3453   return PR_DECLINED(cmd);
3454 }
3455 
3456 MODRET radius_getpwuid(cmd_rec *cmd) {
3457 
3458   if (radius_have_user_info) {
3459 
3460     /* Check that given UID matches faked UID before returning. */
3461     if (*((uid_t *) cmd->argv[0]) == radius_passwd.pw_uid) {
3462 
3463       /* Return the faked user information. */
3464       return mod_create_data(cmd, &radius_passwd);
3465     }
3466   }
3467 
3468   /* Default response */
3469   return PR_DECLINED(cmd);
3470 }
3471 
3472 MODRET radius_setpwent(cmd_rec *cmd) {
3473   return PR_DECLINED(cmd);
3474 }
3475 
3476 /* Command handlers
3477  */
3478 
3479 /* Handle retrieval of quota-related VSAs from response packets.
3480  */
3481 MODRET radius_quota_lookup(cmd_rec *cmd) {
3482 
3483   if (radius_have_quota_info) {
3484     array_header *quota = make_array(session.pool, 9, sizeof(char *));
3485     *((char **) push_array(quota)) = cmd->argv[0];
3486     *((char **) push_array(quota)) = radius_quota_per_sess;
3487     *((char **) push_array(quota)) = radius_quota_limit_type;
3488     *((char **) push_array(quota)) = radius_quota_bytes_in;
3489     *((char **) push_array(quota)) = radius_quota_bytes_out;
3490     *((char **) push_array(quota)) = radius_quota_bytes_xfer;
3491     *((char **) push_array(quota)) = radius_quota_files_in;
3492     *((char **) push_array(quota)) = radius_quota_files_out;
3493     *((char **) push_array(quota)) = radius_quota_files_xfer;
3494 
3495     return mod_create_data(cmd, quota);
3496   }
3497 
3498   return PR_DECLINED(cmd);
3499 }
3500 
3501 /* Perform the check with the RADIUS auth server(s) now, prior to the
3502  * actual handling of the PASS command by mod_auth, so that any of the
3503  * RadiusUserInfo parameters can be supplied by the RADIUS server.
3504  *
3505  * NOTE: first draft, does not honor UserAlias'd names (eg it uses the
3506  * username as supplied by the client.
3507  */
3508 MODRET radius_pre_pass(cmd_rec *cmd) {
3509   int pid_len = 0, sockfd = -1;
3510   radius_packet_t *request = NULL, *response = NULL;
3511   radius_server_t *auth_server = NULL;
3512   unsigned char recvd_response = FALSE;
3513   unsigned int service;
3514   const char *user;
3515   char pid_str[16];
3516 
3517   /* Check to see whether RADIUS authentication should even be done. */
3518   if (radius_engine == FALSE ||
3519       radius_auth_server == NULL) {
3520     return PR_DECLINED(cmd);
3521   }
3522 
3523   user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
3524   if (user == NULL) {
3525     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3526       "missing prerequisite USER command, declining to handle PASS");
3527     pr_response_add_err(R_503, _("Login with USER first"));
3528     return PR_ERROR(cmd);
3529   }
3530 
3531   /* Open a RADIUS socket */
3532   sockfd = radius_open_socket();
3533   if (sockfd < 0) {
3534     int xerrno = errno;
3535     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3536       "socket open failed: %s", strerror(xerrno));
3537     errno = xerrno;
3538     return PR_DECLINED(cmd);
3539   }
3540 
3541   /* Allocate a packet. */
3542   request = (radius_packet_t *) pcalloc(cmd->tmp_pool,
3543     sizeof(radius_packet_t));
3544 
3545   /* Clear the OK flag. */
3546   radius_auth_ok = FALSE;
3547 
3548   memset(pid_str, '\0', sizeof(pid_str));
3549   pid_len = pr_snprintf(pid_str, sizeof(pid_str)-1, "%08u",
3550     (unsigned int) session.pid);
3551 
3552   /* If mod_radius expects to find VSAs in the returned packet, it needs
3553    * to send a service type of Login, otherwise, use the Authenticate-Only
3554    * service type.
3555    */
3556   if (radius_have_user_info == TRUE ||
3557       radius_have_group_info == TRUE ||
3558       radius_have_quota_info == TRUE ||
3559       radius_have_other_info == TRUE) {
3560     service = (unsigned int) htonl(RADIUS_SVC_LOGIN);
3561 
3562   } else {
3563     service = (unsigned int) htonl(RADIUS_SVC_AUTHENTICATE_ONLY);
3564   }
3565 
3566   /* Loop through the list of servers, trying each one until the packet is
3567    * successfully sent.
3568    */
3569   auth_server = radius_auth_server;
3570   while (auth_server != NULL) {
3571     const char *ip_str;
3572 
3573     pr_signals_handle();
3574 
3575     /* Clear the packet. */
3576     memset(request, '\0', sizeof(radius_packet_t));
3577 
3578     /* Build the packet. */
3579     request->code = RADIUS_AUTH_REQUEST;
3580     radius_build_packet(request, radius_realm ?
3581       (const unsigned char *) pstrcat(radius_pool, user, radius_realm, NULL) :
3582       (const unsigned char *) user, (const unsigned char *) cmd->arg,
3583       auth_server->secret, auth_server->secret_len);
3584 
3585     radius_add_attrib(request, RADIUS_SERVICE_TYPE, (unsigned char *) &service,
3586       sizeof(service));
3587 
3588     radius_add_attrib(request, RADIUS_ACCT_SESSION_ID,
3589       (const unsigned char *) pid_str, pid_len);
3590 
3591     /* Calculate the signature. */
3592     radius_set_auth_mac(request, auth_server->secret, auth_server->secret_len);
3593 
3594     /* Send the request. */
3595     ip_str = pr_netaddr_get_ipstr(auth_server->addr);
3596     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3597       "sending auth request packet to %s#%d", ip_str, auth_server->port);
3598     if (radius_send_packet(sockfd, request, auth_server) < 0) {
3599       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3600         "packet send failed to %s#%d", ip_str, auth_server->port);
3601       auth_server = auth_server->next;
3602       continue;
3603     }
3604 
3605     /* Receive the response. */
3606     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3607       "receiving auth response packet from %s#%d", ip_str, auth_server->port);
3608     response = radius_recv_packet(sockfd, auth_server->timeout);
3609     if (response == NULL) {
3610       (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3611         "packet receive failed from %s#%d", ip_str, auth_server->port);
3612       auth_server = auth_server->next;
3613       continue;
3614     }
3615 
3616     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3617       "packet receive succeeded from %s#%d", ip_str, auth_server->port);
3618     recvd_response = TRUE;
3619     break;
3620   }
3621 
3622   /* Close the socket. */
3623   (void) close(sockfd);
3624 
3625   if (recvd_response) {
3626     int res;
3627 
3628     /* Verify the response. */
3629     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3630       "verifying packet");
3631     res = radius_verify_packet(request, response, auth_server->secret,
3632         auth_server->secret_len);
3633     if (res < 0) {
3634       return PR_DECLINED(cmd);
3635     }
3636 
3637     /* Handle the response */
3638     switch (response->code) {
3639       case RADIUS_AUTH_ACCEPT:
3640         /* Process the packet for custom attributes */
3641         res = radius_process_accept_packet(response, auth_server->secret,
3642           auth_server->secret_len);
3643         if (res < 0) {
3644           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3645             "DISCARDING Access-Accept packet for user '%s' due to failed "
3646             "Message-Authenticator check; is the shared secret correct?",
3647             user);
3648           pr_log_pri(PR_LOG_NOTICE, MOD_RADIUS_VERSION
3649             ": DISCARDING Access-Accept packet for user '%s' due to failed "
3650             "Message-Authenticator check; is the shared secret correct?", user);
3651 
3652         } else {
3653           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3654             "authentication successful for user '%s'", user);
3655           pr_trace_msg(trace_channel, 9,
3656             "processed %d %s in Access-Accept packet", res,
3657             res != 1 ? "attributes" : "attribute");
3658 
3659           radius_auth_ok = TRUE;
3660           radius_session_authtype = htonl(RADIUS_AUTH_RADIUS);
3661         }
3662         break;
3663 
3664       case RADIUS_AUTH_REJECT:
3665         /* Process the packet for custom attributes */
3666         res = radius_process_reject_packet(response, auth_server->secret,
3667           auth_server->secret_len);
3668         if (res < 0) {
3669           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3670             "DISCARDING Access-Reject packet for user '%s' due to failed "
3671             "Message-Authenticator check; is the shared secret correct?",
3672             user);
3673           pr_log_pri(PR_LOG_NOTICE, MOD_RADIUS_VERSION
3674             ": DISCARDING Access-Reject packet for user '%s' due to failed "
3675             "Message-Authenticator check; is the shared secret correct?", user);
3676 
3677         } else {
3678           (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3679             "authentication failed for user '%s'", user);
3680           pr_trace_msg(trace_channel, 9,
3681             "processed %d %s in Access-Reject packet", res,
3682             res != 1 ? "attributes" : "attribute");
3683 
3684           radius_auth_ok = FALSE;
3685           radius_auth_reject = TRUE;
3686           radius_reset();
3687         }
3688         break;
3689 
3690       case RADIUS_AUTH_CHALLENGE:
3691         /* Just log this case for now. */
3692         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3693           "authentication challenged for user '%s'", user);
3694         radius_reset();
3695         break;
3696 
3697       default:
3698         (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3699           "notice: server returned unknown response code: %02x",
3700           response->code);
3701         radius_reset();
3702         break;
3703     }
3704 
3705   } else {
3706     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3707       "error: no auth servers responded");
3708   }
3709 
3710   return PR_DECLINED(cmd);
3711 }
3712 
3713 MODRET radius_post_pass(cmd_rec *cmd) {
3714 
3715   /* Check to see if RADIUS accounting should be done. */
3716   if (!radius_engine || !radius_acct_server) {
3717     return PR_DECLINED(cmd);
3718   }
3719 
3720   /* Fill in the username in the faked user info, if need be. */
3721   if (radius_have_user_info) {
3722     radius_passwd.pw_name = (char *) session.user;
3723   }
3724 
3725   if (radius_start_accting() < 0) {
3726     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
3727       "error: unable to start accounting: %s", strerror(errno));
3728   }
3729 
3730   return PR_DECLINED(cmd);
3731 }
3732 
3733 MODRET radius_post_pass_err(cmd_rec *cmd) {
3734   radius_reset();
3735   return PR_DECLINED(cmd);
3736 }
3737 
3738 /* Configuration handlers
3739  */
3740 
3741 /* usage: RadiusAcctServer server[:port] shared-secret [timeout] */
3742 MODRET set_radiusacctserver(cmd_rec *cmd) {
3743   config_rec *c = NULL;
3744   radius_server_t *radius_server = NULL;
3745   unsigned short server_port = 0;
3746   char *port = NULL;
3747 
3748   if (cmd->argc-1 < 2 ||
3749       cmd->argc-1 > 3) {
3750     CONF_ERROR(cmd, "missing parameters");
3751   }
3752 
3753   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3754 
3755   /* Check to see if there's a port specified in the server name */
3756   port = strchr(cmd->argv[1], ':');
3757   if (port != NULL) {
3758 
3759     /* Separate the server name from the port */
3760     *(port++) = '\0';
3761 
3762     server_port = (unsigned short) atoi(port);
3763     if (server_port < 1024) {
3764       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "port number must be greater "
3765         "than 1023", NULL));
3766     }
3767   }
3768 
3769   if (pr_netaddr_get_addr(cmd->tmp_pool, cmd->argv[1], NULL) == NULL) {
3770     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to resolve server address: ",
3771       cmd->argv[1], NULL));
3772   }
3773 
3774   /* Allocate a RADIUS server rec and populate the members */
3775   radius_server = radius_make_server(radius_pool);
3776 
3777   radius_server->addr = pr_netaddr_get_addr(radius_server->pool, cmd->argv[1],
3778     NULL);
3779   radius_server->port = (server_port ? server_port : RADIUS_ACCT_PORT);
3780   radius_server->secret = (unsigned char *) pstrdup(radius_server->pool,
3781     cmd->argv[2]);
3782   radius_server->secret_len = strlen((char *) radius_server->secret);
3783 
3784   if (cmd->argc-1 == 3) {
3785     int timeout = -1;
3786 
3787     if (pr_str_get_duration(cmd->argv[3], &timeout) < 0) {
3788       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3789         cmd->argv[1], "': ", strerror(errno), NULL));
3790     }
3791 
3792     radius_server->timeout = timeout;
3793   }
3794 
3795   c = add_config_param(cmd->argv[0], 1, NULL);
3796   c->argv[0] = pcalloc(c->pool, sizeof(radius_server_t *));
3797   *((radius_server_t **) c->argv[0]) = radius_server;
3798 
3799   return PR_HANDLED(cmd);
3800 }
3801 
3802 /* usage: RadiusAuthServer server[:port] <shared-secret> [timeout] */
3803 MODRET set_radiusauthserver(cmd_rec *cmd) {
3804   config_rec *c = NULL;
3805   radius_server_t *radius_server = NULL;
3806   unsigned short server_port = 0;
3807   char *port = NULL;
3808 
3809   if (cmd->argc-1 < 2 ||
3810       cmd->argc-1 > 3) {
3811     CONF_ERROR(cmd, "missing parameters");
3812   }
3813 
3814   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3815 
3816   /* Check to see if there's a port specified in the server name */
3817   if ((port = strchr(cmd->argv[1], ':')) != NULL) {
3818 
3819     /* Separate the server name from the port */
3820     *(port++) = '\0';
3821 
3822     server_port = (unsigned short) atoi(port);
3823     if (server_port < 1024) {
3824       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "port number must be greater "
3825         "than 1023", NULL));
3826     }
3827   }
3828 
3829   if (pr_netaddr_get_addr(cmd->tmp_pool, cmd->argv[1], NULL) == NULL) {
3830     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable resolve server address: ",
3831       cmd->argv[1], NULL));
3832   }
3833 
3834   /* OK, allocate a RADIUS server rec and populate the members */
3835   radius_server = radius_make_server(radius_pool);
3836 
3837   radius_server->addr = pr_netaddr_get_addr(radius_server->pool, cmd->argv[1],
3838     NULL);
3839   radius_server->port = (server_port ? server_port : RADIUS_AUTH_PORT);
3840   radius_server->secret = (unsigned char *) pstrdup(radius_server->pool,
3841     cmd->argv[2]);
3842   radius_server->secret_len = strlen((char *) radius_server->secret);
3843 
3844   if (cmd->argc-1 == 3) {
3845     int timeout = -1;
3846 
3847     if (pr_str_get_duration(cmd->argv[3], &timeout) < 0) {
3848       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3849         cmd->argv[1], "': ", strerror(errno), NULL));
3850     }
3851 
3852     radius_server->timeout = timeout;
3853   }
3854 
3855   c = add_config_param(cmd->argv[0], 1, NULL);
3856   c->argv[0] = pcalloc(c->pool, sizeof(radius_server_t *));
3857   *((radius_server_t **) c->argv[0]) = radius_server;
3858 
3859   return PR_HANDLED(cmd);
3860 }
3861 
3862 /* usage: RadiusEngine on|off */
3863 MODRET set_radiusengine(cmd_rec *cmd) {
3864   int engine = -1;
3865   config_rec *c = NULL;
3866 
3867   CHECK_ARGS(cmd, 1);
3868   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3869 
3870   engine = get_boolean(cmd, 1);
3871   if (engine == -1) {
3872     CONF_ERROR(cmd, "expected Boolean parameter");
3873   }
3874 
3875   c = add_config_param(cmd->argv[0], 1, NULL);
3876   c->argv[0] = pcalloc(c->pool, sizeof(int));
3877   *((int *) c->argv[0]) = engine;
3878 
3879   return PR_HANDLED(cmd);
3880 }
3881 
3882 /* usage: RadiusGroupInfo primary-name addl-names add-ids */
3883 MODRET set_radiusgroupinfo(cmd_rec *cmd) {
3884   config_rec *c = NULL;
3885   unsigned char group_names_vsa = FALSE;
3886   unsigned char group_ids_vsa = FALSE;
3887   unsigned int ngroups = 0, ngids = 0;
3888 
3889   CHECK_ARGS(cmd, 3);
3890   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3891 
3892   group_names_vsa = radius_have_var(cmd->argv[2]);
3893   group_ids_vsa = radius_have_var(cmd->argv[3]);
3894 
3895   /* There will be five parameters to this config_rec:
3896    *
3897    *  primary-group-name
3898    *  addl-group-name-count
3899    *  addl-group-names
3900    *  addl-group-id-count
3901    *  addl-group-ids
3902    */
3903 
3904   c = add_config_param(cmd->argv[0], 5, NULL, NULL, NULL, NULL, NULL);
3905   c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
3906   c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3907   c->argv[3] = pcalloc(c->pool, sizeof(unsigned int));
3908 
3909   if (group_names_vsa) {
3910     /* As VSA variables, the group names won't be resolved until session time,
3911      * so just store the variable strings as is.
3912      */
3913     c->argv[2] = pstrdup(c->pool, cmd->argv[2]);
3914 
3915   } else {
3916     char **groups = NULL;
3917 
3918     if (!radius_parse_groups_str(c->pool, cmd->argv[2], &groups, &ngroups))
3919       CONF_ERROR(cmd, "badly formatted group names");
3920 
3921     *((unsigned int *) c->argv[1]) = ngroups;
3922     c->argv[2] = (void *) groups;
3923   }
3924 
3925   if (group_ids_vsa) {
3926     /* As VSA variables, the group IDs won't be resolved until session time,
3927      * so just store the variable strings as is.
3928      */
3929     c->argv[4] = pstrdup(c->pool, cmd->argv[3]);
3930 
3931   } else {
3932     gid_t *gids = NULL;
3933 
3934     if (!radius_parse_gids_str(c->pool, cmd->argv[3], &gids, &ngids))
3935       CONF_ERROR(cmd, "badly formatted group IDs");
3936 
3937     *((unsigned int *) c->argv[3]) = ngids;
3938     c->argv[4] = (void *) gids;
3939   }
3940 
3941   if (ngroups > 0 &&
3942       ngids > 0 &&
3943       ngroups != ngids) {
3944     char ngroups_str[32], ngids_str[32];
3945 
3946     memset(ngroups_str, '\0', sizeof(ngroups_str));
3947     pr_snprintf(ngroups_str, sizeof(ngroups_str)-1, "%u", ngroups);
3948 
3949     memset(ngids_str, '\0', sizeof(ngids_str));
3950     pr_snprintf(ngids_str, sizeof(ngids_str)-1, "%u", ngids);
3951 
3952     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "mismatched number of group names (",
3953       ngroups_str, ") and group IDs (", ngids_str, ")", NULL));
3954   }
3955 
3956   return PR_HANDLED(cmd);
3957 }
3958 
3959 /* usage: RadiusLog file|"none" */
3960 MODRET set_radiuslog(cmd_rec *cmd) {
3961   if (cmd->argc-1 != 1)
3962     CONF_ERROR(cmd, "wrong number of parameters");
3963   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3964 
3965   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3966   return PR_HANDLED(cmd);
3967 }
3968 
3969 /* usage: RadiusNASIdentifier string */
3970 MODRET set_radiusnasidentifier(cmd_rec *cmd) {
3971   CHECK_ARGS(cmd, 1);
3972   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3973 
3974   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3975   return PR_HANDLED(cmd);
3976 }
3977 
3978 /* usage: RadiusOptions opt1 ... */
3979 MODRET set_radiusoptions(cmd_rec *cmd) {
3980   config_rec *c = NULL;
3981   register unsigned int i = 0;
3982   unsigned long opts = 0UL;
3983 
3984   if (cmd->argc-1 == 0)
3985     CONF_ERROR(cmd, "wrong number of parameters");
3986 
3987   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3988 
3989   c = add_config_param(cmd->argv[0], 1, NULL);
3990 
3991   for (i = 1; i < cmd->argc; i++) {
3992     if (strcmp(cmd->argv[i], "IgnoreReplyMessage") == 0) {
3993       opts |= RADIUS_OPT_IGNORE_REPLY_MESSAGE_ATTR;
3994 
3995     } else if (strcmp(cmd->argv[i], "IgnoreClass") == 0) {
3996       opts |= RADIUS_OPT_IGNORE_CLASS_ATTR;
3997 
3998     } else if (strcmp(cmd->argv[i], "IgnoreIdleTimeout") == 0) {
3999       opts |= RADIUS_OPT_IGNORE_IDLE_TIMEOUT_ATTR;
4000 
4001     } else if (strcmp(cmd->argv[i], "IgnoreSessionTimeout") == 0) {
4002       opts |= RADIUS_OPT_IGNORE_SESSION_TIMEOUT_ATTR;
4003 
4004     } else if (strcmp(cmd->argv[i], "RequireMAC") == 0) {
4005       opts |= RADIUS_OPT_REQUIRE_MAC;
4006 
4007     } else {
4008       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown TLSOption '",
4009         cmd->argv[i], "'", NULL));
4010     }
4011   }
4012 
4013   c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
4014   *((unsigned long *) c->argv[0]) = opts;
4015 
4016   return PR_HANDLED(cmd);
4017 }
4018 
4019 /* usage: RadiusQuotaInfo per-sess limit-type bytes-in bytes-out bytes-xfer
4020  *          files-in files-out files-xfer
4021  */
4022 MODRET set_radiusquotainfo(cmd_rec *cmd) {
4023   CHECK_ARGS(cmd, 8);
4024   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4025 
4026   if (!radius_have_var(cmd->argv[1])) {
4027     if (strcasecmp(cmd->argv[1], "false") != 0 &&
4028         strcasecmp(cmd->argv[1], "true") != 0)
4029       CONF_ERROR(cmd, "invalid per-session value");
4030   }
4031 
4032   if (!radius_have_var(cmd->argv[2])) {
4033     if (strcasecmp(cmd->argv[2], "hard") != 0 &&
4034         strcasecmp(cmd->argv[2], "soft") != 0) {
4035       CONF_ERROR(cmd, "invalid limit type value");
4036     }
4037   }
4038 
4039   if (!radius_have_var(cmd->argv[3])) {
4040     char *endp = NULL;
4041 
4042     /* Make sure it's a number, at least. */
4043     if (strtod(cmd->argv[3], &endp) < 0) {
4044       CONF_ERROR(cmd, "negative bytes value not allowed");
4045     }
4046 
4047     if (endp && *endp) {
4048       CONF_ERROR(cmd, "invalid bytes parameter: not a number");
4049     }
4050   }
4051 
4052   if (!radius_have_var(cmd->argv[4])) {
4053     char *endp = NULL;
4054 
4055     /* Make sure it's a number, at least. */
4056     if (strtod(cmd->argv[4], &endp) < 0) {
4057       CONF_ERROR(cmd, "negative bytes value not allowed");
4058     }
4059 
4060     if (endp && *endp) {
4061       CONF_ERROR(cmd, "invalid bytes parameter: not a number");
4062     }
4063   }
4064 
4065   if (!radius_have_var(cmd->argv[5])) {
4066     char *endp = NULL;
4067 
4068     /* Make sure it's a number, at least. */
4069     if (strtod(cmd->argv[5], &endp) < 0) {
4070       CONF_ERROR(cmd, "negative bytes value not allowed");
4071     }
4072 
4073     if (endp && *endp) {
4074       CONF_ERROR(cmd, "invalid bytes parameter: not a number");
4075     }
4076   }
4077 
4078   if (!radius_have_var(cmd->argv[6])) {
4079     char *endp = NULL;
4080 
4081     /* Make sure it's a number, at least. */
4082     (void) strtoul(cmd->argv[6], &endp, 10);
4083     if (endp && *endp) {
4084       CONF_ERROR(cmd, "invalid files parameter: not a number");
4085     }
4086   }
4087 
4088   if (!radius_have_var(cmd->argv[7])) {
4089     char *endp = NULL;
4090 
4091     /* Make sure it's a number, at least. */
4092     (void) strtoul(cmd->argv[7], &endp, 10);
4093     if (endp && *endp) {
4094       CONF_ERROR(cmd, "invalid files parameter: not a number");
4095     }
4096   }
4097 
4098   if (!radius_have_var(cmd->argv[8])) {
4099     char *endp = NULL;
4100 
4101     /* Make sure it's a number, at least. */
4102     (void) strtoul(cmd->argv[8], &endp, 10);
4103     if (endp && *endp) {
4104       CONF_ERROR(cmd, "invalid files parameter: not a number");
4105     }
4106   }
4107 
4108   add_config_param_str(cmd->argv[0], 8, cmd->argv[1], cmd->argv[2],
4109     cmd->argv[3], cmd->argv[4], cmd->argv[5], cmd->argv[6],
4110     cmd->argv[7], cmd->argv[8]);
4111 
4112   return PR_HANDLED(cmd);
4113 }
4114 
4115 /* usage: RadiusRealm string */
4116 MODRET set_radiusrealm(cmd_rec *cmd) {
4117   CHECK_ARGS(cmd, 1);
4118   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4119 
4120   add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
4121   return PR_HANDLED(cmd);
4122 }
4123 
4124 /* usage: RadiusUserInfo uid gid home shell */
4125 MODRET set_radiususerinfo(cmd_rec *cmd) {
4126   CHECK_ARGS(cmd, 4);
4127   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4128 
4129   if (!radius_have_var(cmd->argv[1])) {
4130     char *endp = NULL;
4131 
4132     /* Make sure it's a number, at least. */
4133     (void) strtoul(cmd->argv[1], &endp, 10);
4134     if (endp && *endp) {
4135       CONF_ERROR(cmd, "invalid UID parameter: not a number");
4136     }
4137   }
4138 
4139   if (!radius_have_var(cmd->argv[2])) {
4140     char *endp = NULL;
4141 
4142     /* Make sure it's a number, at least. */
4143     (void) strtoul(cmd->argv[2], &endp, 10);
4144     if (endp && *endp)
4145       CONF_ERROR(cmd, "invalid GID parameter: not a number");
4146   }
4147 
4148   if (!radius_have_var(cmd->argv[3])) {
4149     char *path;
4150 
4151     path = cmd->argv[3];
4152     /* Make sure the path is absolute, at least. */
4153     if (*path != '/') {
4154       CONF_ERROR(cmd, "home relative path not allowed");
4155     }
4156   }
4157 
4158   if (!radius_have_var(cmd->argv[4])) {
4159     char *path;
4160 
4161     path = cmd->argv[4];
4162     /* Make sure the path is absolute, at least. */
4163     if (*path != '/') {
4164       CONF_ERROR(cmd, "shell relative path not allowed");
4165     }
4166   }
4167 
4168   add_config_param_str(cmd->argv[0], 4, cmd->argv[1], cmd->argv[2],
4169     cmd->argv[3], cmd->argv[4]);
4170   return PR_HANDLED(cmd);
4171 }
4172 
4173 /* usage: RadiusVendor name id */
4174 MODRET set_radiusvendor(cmd_rec *cmd) {
4175   config_rec *c = NULL;
4176   long id = 0;
4177   char *tmp = NULL;
4178 
4179   CHECK_ARGS(cmd, 2);
4180   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4181 
4182   /* Make sure that the given vendor ID number is valid. */
4183   id = strtol(cmd->argv[2], &tmp, 10);
4184 
4185   if (tmp && *tmp) {
4186     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": vendor id '", cmd->argv[2],
4187       "' is not a valid number", NULL));
4188   }
4189 
4190   if (id < 0) {
4191     CONF_ERROR(cmd, "vendor id must be a positive number");
4192   }
4193 
4194   c = add_config_param(cmd->argv[0], 2, NULL, NULL);
4195   c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
4196   c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
4197   *((unsigned int *) c->argv[1]) = id;
4198 
4199   return PR_HANDLED(cmd);
4200 }
4201 
4202 /* Event handlers
4203  */
4204 
4205 static void radius_exit_ev(const void *event_data, void *user_data) {
4206   if (radius_stop_accting() < 0) {
4207     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
4208       "error: unable to stop accounting: %s", strerror(errno));
4209   }
4210 
4211   (void) close(radius_logfd);
4212   radius_logfd = -1;
4213 }
4214 
4215 #if defined(PR_SHARED_MODULE)
4216 static void radius_mod_unload_ev(const void *event_data, void *user_data) {
4217   if (strcmp("mod_radius.c", (const char *) event_data) == 0) {
4218     pr_event_unregister(&radius_module, NULL, NULL);
4219 
4220     if (radius_pool) {
4221       destroy_pool(radius_pool);
4222       radius_pool = NULL;
4223     }
4224 
4225     close(radius_logfd);
4226     radius_logfd = -1;
4227   }
4228 }
4229 #endif /* PR_SHARED_MODULE */
4230 
4231 static void radius_restart_ev(const void *event_data, void *user_data) {
4232 
4233   /* Re-allocate the pool used by this module. */
4234   if (radius_pool) {
4235     destroy_pool(radius_pool);
4236   }
4237 
4238   radius_pool = make_sub_pool(permanent_pool);
4239   pr_pool_tag(radius_pool, MOD_RADIUS_VERSION);
4240 
4241   return;
4242 }
4243 
4244 static void radius_sess_reinit_ev(const void *event_data, void *user_data) {
4245   int res;
4246 
4247   /* A HOST command changed the main_server pointer; reinitialize ourselves. */
4248 
4249   pr_event_unregister(&radius_module, "core.exit", radius_exit_ev);
4250   pr_event_unregister(&radius_module, "core.session-reinit",
4251     radius_sess_reinit_ev);
4252 
4253   /* Reset defaults. */
4254   radius_engine = FALSE;
4255   radius_acct_server = NULL;
4256   radius_auth_server = NULL;
4257   (void) close(radius_logfd);
4258   radius_logfd = -1;
4259   radius_opts = 0UL;
4260   radius_nas_identifier_config = NULL;
4261   radius_vendor_name = "Unix";
4262   radius_vendor_id = 4;
4263   radius_realm = NULL;
4264 
4265   radius_have_user_info = FALSE;
4266   radius_uid_attr_id = 0;
4267   radius_gid_attr_id = 0;
4268   radius_home_attr_id = 0;
4269   radius_shell_attr_id = 0;
4270 
4271   radius_have_group_info = FALSE;
4272   radius_prime_group_name_attr_id = 0;
4273   radius_addl_group_names_attr_id = 0;
4274   radius_addl_group_ids_attr_id = 0;
4275   radius_prime_group_name = NULL;
4276   radius_addl_group_count = 0;
4277   radius_addl_group_names = 0;
4278   radius_addl_group_names_str = NULL;
4279   radius_addl_group_ids = NULL;
4280   radius_addl_group_ids_str = NULL;
4281 
4282   radius_have_quota_info = FALSE;
4283   radius_quota_per_sess_attr_id = 0;
4284   radius_quota_limit_type_attr_id = 0;
4285   radius_quota_bytes_in_attr_id = 0;
4286   radius_quota_bytes_out_attr_id = 0;
4287   radius_quota_bytes_xfer_attr_id = 0;
4288   radius_quota_files_in_attr_id = 0;
4289   radius_quota_files_out_attr_id = 0;
4290   radius_quota_files_xfer_attr_id = 0;
4291   radius_quota_per_sess = NULL;
4292   radius_quota_limit_type = NULL;
4293   radius_quota_bytes_in = NULL;
4294   radius_quota_bytes_out = NULL;
4295   radius_quota_bytes_xfer = NULL;
4296   radius_quota_files_in = NULL;
4297   radius_quota_files_out = NULL;
4298   radius_quota_files_xfer = NULL;
4299 
4300   radius_have_other_info = FALSE;
4301 
4302   /* Note that we deliberately leave the radius_session_start time_t alone;
4303    * it is initialized at the start of the session, regardless of vhost.
4304    */
4305 
4306   res = radius_sess_init();
4307   if (res < 0) {
4308     pr_session_disconnect(&radius_module,
4309       PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
4310   }
4311 }
4312 
4313 /* Initialization routines
4314  */
4315 
4316 static int radius_sess_init(void) {
4317   int res = 0;
4318   config_rec *c = NULL;
4319   radius_server_t **current_server = NULL;
4320 
4321   pr_event_register(&radius_module, "core.session-reinit",
4322     radius_sess_reinit_ev, NULL);
4323 
4324   /* Is RadiusEngine on? */
4325   c = find_config(main_server->conf, CONF_PARAM, "RadiusEngine", FALSE);
4326   if (c != NULL) {
4327     radius_engine = *((int *) c->argv[0]);
4328   }
4329 
4330   if (radius_engine == FALSE) {
4331     return 0;
4332   }
4333 
4334   res = radius_openlog();
4335   if (res < 0) {
4336     if (res == -1) {
4337       pr_log_pri(PR_LOG_NOTICE, MOD_RADIUS_VERSION
4338         ": notice: unable to open RadiusLog: %s", strerror(errno));
4339 
4340     } else if (res == PR_LOG_WRITABLE_DIR) {
4341       pr_log_pri(PR_LOG_WARNING, MOD_RADIUS_VERSION
4342         ": notice: unable to open RadiusLog: parent directory is "
4343         "world-writable");
4344 
4345     } else if (res == PR_LOG_SYMLINK) {
4346       pr_log_pri(PR_LOG_WARNING, MOD_RADIUS_VERSION
4347         ": notice: unable to open RadiusLog: cannot log to a symbolic link");
4348     }
4349   }
4350 
4351   /* Initialize session variables */
4352   time(&radius_session_start);
4353 
4354   c = find_config(main_server->conf, CONF_PARAM, "RadiusOptions", FALSE);
4355   while (c != NULL) {
4356     unsigned long opts = 0;
4357 
4358     pr_signals_handle();
4359 
4360     opts = *((unsigned long *) c->argv[0]);
4361     radius_opts |= opts;
4362 
4363     c = find_config_next(c, c->next, CONF_PARAM, "RadiusOptions", FALSE);
4364   }
4365 
4366   c = find_config(main_server->conf, CONF_PARAM, "RadiusNASIdentifier", FALSE);
4367   if (c != NULL) {
4368     radius_nas_identifier_config = c->argv[0];
4369 
4370     pr_trace_msg(trace_channel, 3,
4371       "RadiusNASIdentifier '%s' configured", radius_nas_identifier_config);
4372   }
4373 
4374   c = find_config(main_server->conf, CONF_PARAM, "RadiusVendor", FALSE);
4375   if (c != NULL) {
4376     radius_vendor_name = c->argv[0];
4377     radius_vendor_id = *((unsigned int *) c->argv[1]);
4378 
4379     pr_trace_msg(trace_channel, 3,
4380       "RadiusVendor '%s' (Vendor-Id %u) configured", radius_vendor_name,
4381       radius_vendor_id);
4382   }
4383 
4384   /* Find any configured RADIUS servers for this session */
4385   c = find_config(main_server->conf, CONF_PARAM, "RadiusAcctServer", FALSE);
4386 
4387   /* Point to the start of the accounting server list. */
4388   current_server = &radius_acct_server;
4389 
4390   while (c != NULL) {
4391     pr_signals_handle();
4392 
4393     *current_server = *((radius_server_t **) c->argv[0]);
4394     current_server = &(*current_server)->next;
4395 
4396     c = find_config_next(c, c->next, CONF_PARAM, "RadiusAcctServer", FALSE);
4397   }
4398 
4399   if (radius_acct_server == NULL) {
4400     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
4401       "notice: no configured RadiusAcctServers, no accounting");
4402   }
4403 
4404   c = find_config(main_server->conf, CONF_PARAM, "RadiusAuthServer", FALSE);
4405 
4406   /* Point to the start of the authentication server list. */
4407   current_server = &radius_auth_server;
4408 
4409   while (c != NULL) {
4410     pr_signals_handle();
4411 
4412     *current_server = *((radius_server_t **) c->argv[0]);
4413     current_server = &(*current_server)->next;
4414 
4415     c = find_config_next(c, c->next, CONF_PARAM, "RadiusAuthServer", FALSE);
4416   }
4417 
4418   if (radius_auth_server == NULL) {
4419     (void) pr_log_writefile(radius_logfd, MOD_RADIUS_VERSION,
4420       "notice: no configured RadiusAuthServers, no authentication");
4421   }
4422 
4423   /* Prepare any configured fake user information. */
4424   c = find_config(main_server->conf, CONF_PARAM, "RadiusUserInfo", FALSE);
4425   if (c != NULL) {
4426 
4427     /* Process the parameter string stored in the found config_rec. */
4428     radius_process_user_info(c);
4429 
4430     /* Only use the faked information if authentication via RADIUS is
4431      * possible.  The radius_have_user_info flag will be set to
4432      * TRUE by radius_process_user_info(), unless there was some
4433      * illegal value.
4434      */
4435     if (radius_auth_server == NULL) {
4436       radius_have_user_info = FALSE;
4437     }
4438   }
4439 
4440   /* If the RadiusUserInfo directive has not been set (or if it has been
4441    * set, but it was not well-formed), then we will be acting in a
4442    * "yes/no" style of authentication, similar to PAM.
4443    *
4444    * The Auth API tries to use the same module for authenticating a user
4445    * as the one which provided information for that user.  If we are not
4446    * providing user information, then we won't get a chance to authenticate
4447    * the user -- unless we disable that Auth API behavior.
4448    */
4449   if (radius_have_user_info == FALSE) {
4450     if (pr_auth_add_auth_only_module("mod_radius.c") < 0) {
4451       pr_log_debug(DEBUG2, "error adding 'mod_radius.c' to auth-only module "
4452         "list: %s", strerror(errno));
4453     }
4454   }
4455 
4456   /* Prepare any configured fake group information. */
4457   c = find_config(main_server->conf, CONF_PARAM, "RadiusGroupInfo", FALSE);
4458   if (c != NULL) {
4459 
4460     /* Process the parameter string stored in the found config_rec. */
4461     radius_process_group_info(c);
4462 
4463     /* Only use the faked information if authentication via RADIUS is
4464      * possible.  The radius_have_group_info flag will be set to
4465      * TRUE by radius_process_group_info(), unless there was some
4466      * illegal value.
4467      */
4468     if (radius_auth_server == NULL) {
4469       radius_have_group_info = FALSE;
4470     }
4471   }
4472 
4473   /* Prepare any configure quota information. */
4474   c = find_config(main_server->conf, CONF_PARAM, "RadiusQuotaInfo", FALSE);
4475   if (c != NULL) {
4476     radius_process_quota_info(c);
4477 
4478     if (radius_auth_server == NULL) {
4479       radius_have_quota_info = FALSE;
4480     }
4481   }
4482 
4483   /* Check for a configured RadiusRealm.  If present, use username + realm
4484    * in RADIUS packets as the user name, else just use the username.
4485    */
4486   radius_realm = get_param_ptr(main_server->conf, "RadiusRealm", FALSE);
4487   if (radius_realm) {
4488     pr_trace_msg(trace_channel, 3,
4489       "using RadiusRealm '%s'", radius_realm);
4490   }
4491 
4492   pr_event_register(&radius_module, "core.exit", radius_exit_ev, NULL);
4493   return 0;
4494 }
4495 
4496 static int radius_init(void) {
4497 
4498   /* Allocate a pool for this module's use. */
4499   radius_pool = make_sub_pool(permanent_pool);
4500   pr_pool_tag(radius_pool, MOD_RADIUS_VERSION);
4501 
4502 #if defined(PR_SHARED_MODULE)
4503   pr_event_register(&radius_module, "core.module-unload",
4504     radius_mod_unload_ev, NULL);
4505 #endif /* PR_SHARED_MODULE */
4506 
4507   /* Register a restart handler, to cleanup the pool. */
4508   pr_event_register(&radius_module, "core.restart", radius_restart_ev, NULL);
4509 
4510   return 0;
4511 }
4512 
4513 /* Module API tables
4514  */
4515 
4516 static conftable radius_conftab[] = {
4517   { "RadiusAcctServer", 	set_radiusacctserver,	NULL },
4518   { "RadiusAuthServer", 	set_radiusauthserver,	NULL },
4519   { "RadiusEngine",		set_radiusengine,	NULL },
4520   { "RadiusGroupInfo",		set_radiusgroupinfo,	NULL },
4521   { "RadiusLog",		set_radiuslog,		NULL },
4522   { "RadiusNASIdentifier",	set_radiusnasidentifier,NULL },
4523   { "RadiusOptions",		set_radiusoptions,	NULL },
4524   { "RadiusQuotaInfo",		set_radiusquotainfo,	NULL },
4525   { "RadiusRealm",		set_radiusrealm,	NULL },
4526   { "RadiusUserInfo",		set_radiususerinfo,	NULL },
4527   { "RadiusVendor",		set_radiusvendor,	NULL },
4528   { NULL }
4529 };
4530 
4531 static cmdtable radius_cmdtab[] = {
4532   { HOOK,		"radius_quota_lookup", G_NONE,
4533       radius_quota_lookup, FALSE, FALSE },
4534 
4535   { PRE_CMD,		C_PASS, G_NONE, radius_pre_pass,	FALSE, FALSE, CL_AUTH },
4536   { POST_CMD,		C_PASS, G_NONE, radius_post_pass, 	FALSE, FALSE, CL_AUTH },
4537   { POST_CMD_ERR,	C_PASS, G_NONE, radius_post_pass_err, 	FALSE, FALSE, CL_AUTH },
4538   { 0, NULL }
4539 };
4540 
4541 static authtable radius_authtab[] = {
4542   { 0, "setpwent",  radius_setpwent },
4543   { 0, "setgrent",  radius_setgrent },
4544   { 0, "endpwent",  radius_endpwent },
4545   { 0, "endgrent",  radius_endgrent },
4546   { 0, "getpwent",  radius_getpwent },
4547   { 0, "getgrent",  radius_getgrent },
4548   { 0, "getpwnam",  radius_getpwnam },
4549   { 0, "getgrnam",  radius_getgrnam },
4550   { 0, "getpwuid",  radius_getpwuid },
4551   { 0, "getgrgid",  radius_getgrgid },
4552   { 0, "getgroups", radius_getgroups },
4553   { 0, "auth",      radius_auth     },
4554   { 0, "check",     radius_check    },
4555   { 0, "uid2name",  radius_uid2name },
4556   { 0, "gid2name",  radius_gid2name },
4557   { 0, "name2uid",  radius_name2uid },
4558   { 0, "name2gid",  radius_name2gid },
4559   { 0, NULL }
4560 };
4561 
4562 module radius_module = {
4563 
4564   /* Always NULL */
4565   NULL, NULL,
4566 
4567   /* Module API version 2.0 */
4568   0x20,
4569 
4570   /* Module name */
4571   "radius",
4572 
4573   /* Module configuration handler table */
4574   radius_conftab,
4575 
4576   /* Module command handler table */
4577   radius_cmdtab,
4578 
4579   /* Module authentication handler table */
4580   radius_authtab,
4581 
4582   /* Module initialization function */
4583   radius_init,
4584 
4585   /* Module session initialization function */
4586   radius_sess_init,
4587 
4588   /* Module version */
4589   MOD_RADIUS_VERSION
4590 };
4591