1 /*
2  * ProFTPD - FTP server daemon
3  * Copyright (c) 1997, 1998 Public Flood Software
4  * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5  * Copyright (c) 2001-2021 The ProFTPD Project team
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20  *
21  * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 /* Authentication module for ProFTPD */
28 
29 #include "conf.h"
30 #include "privs.h"
31 
32 #ifdef HAVE_USERSEC_H
33 # include <usersec.h>
34 #endif
35 
36 #ifdef HAVE_SYS_AUDIT_H
37 # include <sys/audit.h>
38 #endif
39 
40 extern pid_t mpid;
41 
42 module auth_module;
43 
44 #ifdef PR_USE_LASTLOG
45 static unsigned char lastlog = FALSE;
46 #endif /* PR_USE_LASTLOG */
47 
48 static unsigned char mkhome = FALSE;
49 static unsigned char authenticated_without_pass = FALSE;
50 static int TimeoutLogin = PR_TUNABLE_TIMEOUTLOGIN;
51 static int logged_in = FALSE;
52 static int auth_anon_allow_robots = FALSE;
53 static int auth_anon_allow_robots_enabled = FALSE;
54 static int auth_client_connected = FALSE;
55 static int auth_tries = 0;
56 static char *auth_pass_resp_code = R_230;
57 static pr_fh_t *displaylogin_fh = NULL;
58 static int TimeoutSession = 0;
59 
60 static int saw_first_user_cmd = FALSE;
61 static const char *timing_channel = "timing";
62 
63 static int auth_count_scoreboard(cmd_rec *, const char *);
64 static int auth_scan_scoreboard(void);
65 static int auth_sess_init(void);
66 
67 /* auth_cmd_chk_cb() is hooked into the main server's auth_hook function,
68  * so that we can deny all commands until authentication is complete.
69  *
70  * Note: Once this function returns true (i.e. client has authenticated),
71  * it will ALWAYS return true.  At least until REIN is implemented.  Thus
72  * we have a flag for such a situation, to save on redundant lookups for
73  * the "authenticated" record.
74  */
75 static int auth_have_authenticated = FALSE;
76 
auth_cmd_chk_cb(cmd_rec * cmd)77 static int auth_cmd_chk_cb(cmd_rec *cmd) {
78   if (auth_have_authenticated == FALSE) {
79     unsigned char *authd;
80 
81     authd = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
82 
83     if (authd == NULL ||
84         *authd == FALSE) {
85       pr_response_send(R_530, _("Please login with USER and PASS"));
86       return FALSE;
87     }
88 
89     auth_have_authenticated = TRUE;
90   }
91 
92   return TRUE;
93 }
94 
auth_login_timeout_cb(CALLBACK_FRAME)95 static int auth_login_timeout_cb(CALLBACK_FRAME) {
96   pr_response_send_async(R_421,
97     _("Login timeout (%d %s): closing control connection"), TimeoutLogin,
98     TimeoutLogin != 1 ? "seconds" : "second");
99 
100   /* It's possible that any listeners of this event might terminate the
101    * session process themselves (e.g. mod_ban).  So write out that the
102    * TimeoutLogin has been exceeded to the log here, in addition to the
103    * scheduled session exit message.
104    */
105   pr_log_pri(PR_LOG_INFO, "%s", "Login timeout exceeded, disconnected");
106   pr_event_generate("core.timeout-login", NULL);
107 
108   pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_TIMEOUT,
109     "TimeoutLogin");
110 
111   /* Do not restart the timer (should never be reached). */
112   return 0;
113 }
114 
auth_session_timeout_cb(CALLBACK_FRAME)115 static int auth_session_timeout_cb(CALLBACK_FRAME) {
116   pr_event_generate("core.timeout-session", NULL);
117   pr_response_send_async(R_421,
118     _("Session Timeout (%d seconds): closing control connection"),
119     TimeoutSession);
120 
121   pr_log_pri(PR_LOG_INFO, "%s", "FTP session timed out, disconnected");
122   pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_TIMEOUT,
123     "TimeoutSession");
124 
125   /* no need to restart the timer -- session's over */
126   return 0;
127 }
128 
129 /* Event listeners
130  */
131 
auth_exit_ev(const void * event_data,void * user_data)132 static void auth_exit_ev(const void *event_data, void *user_data) {
133   pr_auth_cache_clear();
134 
135   /* Close the scoreboard descriptor that we opened. */
136   (void) pr_close_scoreboard(FALSE);
137 }
138 
auth_sess_reinit_ev(const void * event_data,void * user_data)139 static void auth_sess_reinit_ev(const void *event_data, void *user_data) {
140   int res;
141 
142   /* A HOST command changed the main_server pointer, reinitialize ourselves. */
143 
144   pr_event_unregister(&auth_module, "core.exit", auth_exit_ev);
145   pr_event_unregister(&auth_module, "core.session-reinit", auth_sess_reinit_ev);
146 
147   pr_timer_remove(PR_TIMER_LOGIN, &auth_module);
148 
149   /* Reset the CreateHome setting. */
150   mkhome = FALSE;
151 
152   /* Reset any MaxPasswordSize setting. */
153   (void) pr_auth_set_max_password_len(session.pool, 0);
154 
155 #if defined(PR_USE_LASTLOG)
156   lastlog = FALSE;
157 #endif /* PR_USE_LASTLOG */
158   mkhome = FALSE;
159 
160   res = auth_sess_init();
161   if (res < 0) {
162     pr_session_disconnect(&auth_module,
163       PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
164   }
165 }
166 
167 /* Initialization functions
168  */
169 
auth_init(void)170 static int auth_init(void) {
171   /* Add the commands handled by this module to the HELP list. */
172   pr_help_add(C_USER, _("<sp> username"), TRUE);
173   pr_help_add(C_PASS, _("<sp> password"), TRUE);
174   pr_help_add(C_ACCT, _("is not implemented"), FALSE);
175   pr_help_add(C_REIN, _("is not implemented"), FALSE);
176 
177   /* By default, enable auth checking */
178   set_auth_check(auth_cmd_chk_cb);
179 
180   return 0;
181 }
182 
auth_sess_init(void)183 static int auth_sess_init(void) {
184   config_rec *c = NULL;
185   unsigned char *tmp = NULL;
186 
187   pr_event_register(&auth_module, "core.session-reinit", auth_sess_reinit_ev,
188     NULL);
189 
190   /* Check for any MaxPasswordSize. */
191   c = find_config(main_server->conf, CONF_PARAM, "MaxPasswordSize", FALSE);
192   if (c != NULL) {
193     size_t len;
194 
195     len = *((size_t *) c->argv[0]);
196     (void) pr_auth_set_max_password_len(session.pool, len);
197   }
198 
199   /* Check for a server-specific TimeoutLogin */
200   c = find_config(main_server->conf, CONF_PARAM, "TimeoutLogin", FALSE);
201   if (c != NULL) {
202     TimeoutLogin = *((int *) c->argv[0]);
203   }
204 
205   /* Start the login timer */
206   if (TimeoutLogin) {
207     pr_timer_remove(PR_TIMER_LOGIN, &auth_module);
208     pr_timer_add(TimeoutLogin, PR_TIMER_LOGIN, &auth_module,
209       auth_login_timeout_cb, "TimeoutLogin");
210   }
211 
212   if (auth_client_connected == FALSE) {
213     int res = 0;
214 
215     PRIVS_ROOT
216     res = pr_open_scoreboard(O_RDWR);
217     PRIVS_RELINQUISH
218 
219     if (res < 0) {
220       switch (res) {
221         case PR_SCORE_ERR_BAD_MAGIC:
222           pr_log_debug(DEBUG0, "error opening scoreboard: bad/corrupted file");
223           break;
224 
225         case PR_SCORE_ERR_OLDER_VERSION:
226           pr_log_debug(DEBUG0,
227             "error opening scoreboard: bad version (too old)");
228           break;
229 
230         case PR_SCORE_ERR_NEWER_VERSION:
231           pr_log_debug(DEBUG0,
232             "error opening scoreboard: bad version (too new)");
233           break;
234 
235         default:
236           pr_log_debug(DEBUG0, "error opening scoreboard: %s", strerror(errno));
237           break;
238       }
239     }
240   }
241 
242   pr_event_register(&auth_module, "core.exit", auth_exit_ev, NULL);
243 
244   if (auth_client_connected == FALSE) {
245     /* Create an entry in the scoreboard for this session, if we don't already
246      * have one.
247      */
248     if (pr_scoreboard_entry_get(PR_SCORE_CLIENT_ADDR) == NULL) {
249       if (pr_scoreboard_entry_add() < 0) {
250         pr_log_pri(PR_LOG_NOTICE, "notice: unable to add scoreboard entry: %s",
251           strerror(errno));
252       }
253 
254       pr_scoreboard_entry_update(session.pid,
255         PR_SCORE_USER, "(none)",
256         PR_SCORE_SERVER_PORT, main_server->ServerPort,
257         PR_SCORE_SERVER_ADDR, session.c->local_addr, session.c->local_port,
258         PR_SCORE_SERVER_LABEL, main_server->ServerName,
259         PR_SCORE_CLIENT_ADDR, session.c->remote_addr,
260         PR_SCORE_CLIENT_NAME, session.c->remote_name,
261         PR_SCORE_CLASS, session.conn_class ? session.conn_class->cls_name : "",
262         PR_SCORE_PROTOCOL, "ftp",
263         PR_SCORE_BEGIN_SESSION, time(NULL),
264         NULL);
265     }
266 
267   } else {
268     /* We're probably handling a HOST command, and the server changed; just
269      * update the SERVER_LABEL field.
270      */
271     pr_scoreboard_entry_update(session.pid,
272       PR_SCORE_SERVER_LABEL, main_server->ServerName,
273       NULL);
274   }
275 
276   /* Should we create the home for a user, if they don't have one? */
277   tmp = get_param_ptr(main_server->conf, "CreateHome", FALSE);
278   if (tmp != NULL &&
279       *tmp == TRUE) {
280     mkhome = TRUE;
281 
282   } else {
283     mkhome = FALSE;
284   }
285 
286 #ifdef PR_USE_LASTLOG
287   /* Use the lastlog file, if supported and requested. */
288   tmp = get_param_ptr(main_server->conf, "UseLastlog", FALSE);
289   if (tmp &&
290       *tmp == TRUE) {
291     lastlog = TRUE;
292 
293   } else {
294     lastlog = FALSE;
295   }
296 #endif /* PR_USE_LASTLOG */
297 
298   /* Scan the scoreboard now, in order to tally up certain values for
299    * substituting in any of the Display* file variables.  This function
300    * also performs the MaxConnectionsPerHost enforcement.
301    */
302   auth_scan_scoreboard();
303 
304   auth_client_connected = TRUE;
305   return 0;
306 }
307 
do_auth(pool * p,xaset_t * conf,const char * u,char * pw)308 static int do_auth(pool *p, xaset_t *conf, const char *u, char *pw) {
309   char *cpw = NULL;
310   config_rec *c;
311 
312   if (conf != NULL) {
313     c = find_config(conf, CONF_PARAM, "UserPassword", FALSE);
314     while (c != NULL) {
315       pr_signals_handle();
316 
317       if (strcmp(c->argv[0], u) == 0) {
318         cpw = (char *) c->argv[1];
319         break;
320       }
321 
322       c = find_config_next(c, c->next, CONF_PARAM, "UserPassword", FALSE);
323     }
324   }
325 
326   if (cpw != NULL) {
327     if (pr_auth_getpwnam(p, u) == NULL) {
328       int xerrno = errno;
329 
330       if (xerrno == ENOENT) {
331         pr_log_pri(PR_LOG_NOTICE, "no such user '%s'", u);
332       }
333 
334       errno = xerrno;
335       return PR_AUTH_NOPWD;
336     }
337 
338     return pr_auth_check(p, cpw, u, pw);
339   }
340 
341   return pr_auth_authenticate(p, u, pw);
342 }
343 
344 /* Command handlers
345  */
346 
login_failed(pool * p,const char * user)347 static void login_failed(pool *p, const char *user) {
348   const char *host, *sess_ttyname;
349 #if defined(HAVE_LOGINFAILED)
350   int res, xerrno;
351 #endif /* HAVE_LOGINFAILED */
352 
353   host = pr_netaddr_get_dnsstr(session.c->remote_addr);
354   sess_ttyname = pr_session_get_ttyname(p);
355 
356   pr_trace_msg("auth", 19, "mod_auth handling failed login for "
357     "user = '%s', host = '%s', tty = '%s'", user, host, sess_ttyname);
358 #if defined(HAVE_LOGINFAILED)
359   PRIVS_ROOT
360   res = loginfailed((char *) user, (char *) host, (char *) sess_ttyname,
361     AUDIT_FAIL);
362   xerrno = errno;
363   PRIVS_RELINQUISH
364 
365   if (res < 0) {
366     pr_trace_msg("auth", 3, "AIX loginfailed() error for user '%s', "
367       "host '%s', tty '%s', reason %d: %s", user, host, sess_ttyname,
368       AUDIT_FAIL, strerror(errno));
369   }
370 #endif /* HAVE_LOGINFAILED */
371 }
372 
auth_err_pass(cmd_rec * cmd)373 MODRET auth_err_pass(cmd_rec *cmd) {
374   const char *user;
375 
376   user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
377   if (user != NULL) {
378     const void *hint;
379 
380     /* Look for any notes/hints attached to this command which might indicate
381      * that it is not a real PASS command error, but rather a fake command
382      * dispatched for e.g. logging/handling by other modules.  We pay attention
383      * to this here due to e.g. AIX loginfailed(3) semantics (Issue #693).
384      */
385     hint = pr_table_get(cmd->notes, "mod_sftp.nonfatal-attempt", NULL);
386     if (hint == NULL) {
387       login_failed(cmd->tmp_pool, user);
388 
389     } else {
390       pr_trace_msg("auth", 19,
391         "ignoring non-fatal %s auth attempt for user '%s' from mod_sftp",
392         (const char *) hint, user);
393     }
394   }
395 
396   /* Remove the stashed original USER name here in a LOG_CMD_ERR handler, so
397    * that other modules, who may want to lookup the original USER parameter on
398    * a failed login in an earlier command handler phase, have a chance to do
399    * so.  This removal of the USER parameter on failure was happening directly
400    * in the CMD handler previously, thus preventing POST_CMD_ERR handlers from
401    * using USER.
402    */
403   pr_table_remove(session.notes, "mod_auth.orig-user", NULL);
404 
405   /* If auth_tries = -1, that means we reached the max login attempts and
406    * should disconnect the session.
407    */
408   if (auth_tries == -1) {
409     pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
410       "Denied by MaxLoginAttempts");
411   }
412 
413   return PR_HANDLED(cmd);
414 }
415 
auth_log_pass(cmd_rec * cmd)416 MODRET auth_log_pass(cmd_rec *cmd) {
417 
418   /* Only log, to the syslog, that the login has succeeded here, where we
419    * know that the login has definitely succeeded.
420    */
421   pr_log_auth(PR_LOG_INFO, "%s %s: Login successful.",
422     (session.anon_config != NULL) ? "ANON" : C_USER, session.user);
423 
424   if (cmd->arg != NULL) {
425     size_t passwd_len;
426 
427     /* And scrub the memory holding the password sent by the client, for
428      * safety/security.
429      */
430     passwd_len = strlen(cmd->arg);
431     pr_memscrub(cmd->arg, passwd_len);
432   }
433 
434   return PR_DECLINED(cmd);
435 }
436 
login_succeeded(pool * p,const char * user)437 static void login_succeeded(pool *p, const char *user) {
438   const char *host, *sess_ttyname;
439 #if defined(HAVE_LOGINSUCCESS)
440   char *msg = NULL;
441   int res, xerrno;
442 #endif /* HAVE_LOGINSUCCESS */
443 
444   host = pr_netaddr_get_dnsstr(session.c->remote_addr);
445   sess_ttyname = pr_session_get_ttyname(p);
446 
447   pr_trace_msg("auth", 19, "mod_auth handling successful login for "
448     "user = '%s', host = '%s', tty = '%s'", user, host, sess_ttyname);
449 
450 #if defined(HAVE_LOGINSUCCESS)
451   PRIVS_ROOT
452   res = loginsuccess((char *) user, (char *) host, (char *) sess_ttyname, &msg);
453   xerrno = errno;
454   PRIVS_RELINQUISH
455 
456   if (res == 0) {
457     if (msg != NULL) {
458       pr_trace_msg("auth", 14, "AIX loginsuccess() report: %s", msg);
459     }
460 
461   } else {
462     pr_trace_msg("auth", 3, "AIX loginsuccess() error for user '%s', "
463       "host '%s', tty '%s': %s", user, host, sess_ttyname, strerror(errno));
464   }
465 
466   if (msg != NULL) {
467     free(msg);
468   }
469 #endif /* HAVE_LOGINSUCCESS */
470 }
471 
auth_post_pass(cmd_rec * cmd)472 MODRET auth_post_pass(cmd_rec *cmd) {
473   config_rec *c = NULL;
474   const char *grantmsg = NULL, *user;
475   unsigned int ctxt_precedence = 0;
476   unsigned char have_user_timeout, have_group_timeout, have_class_timeout,
477     have_all_timeout, *authenticated;
478   int root_revoke = TRUE;
479   struct stat st;
480 
481   /* Was there a preceding USER command? Was the client successfully
482    * authenticated?
483    */
484   authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
485 
486   /* Clear the list of auth-only modules. */
487   pr_auth_clear_auth_only_modules();
488 
489   if (authenticated != NULL &&
490       *authenticated == TRUE) {
491 
492     /* At this point, we can look up the Protocols config if the client
493      * has been authenticated, which may have been tweaked via mod_ifsession's
494      * user/group/class-specific sections.
495      */
496     c = find_config(main_server->conf, CONF_PARAM, "Protocols", FALSE);
497     if (c != NULL) {
498       array_header *protocols;
499       char **elts;
500       const char *protocol;
501 
502       protocols = c->argv[0];
503       elts = protocols->elts;
504 
505       protocol = pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT);
506 
507       /* We only want to check for 'ftp' in the configured Protocols list
508        * if a) a RFC2228 mechanism (e.g. SSL or GSS) is not in use, and
509        *    b) an SSH protocol is not in use.
510        */
511       if (session.rfc2228_mech == NULL &&
512           strncmp(protocol, "SSH2", 5) != 0) {
513         register unsigned int i;
514         int allow_ftp = FALSE;
515 
516         for (i = 0; i < protocols->nelts; i++) {
517           char *proto;
518 
519           proto = elts[i];
520           if (proto != NULL) {
521             if (strncasecmp(proto, "ftp", 4) == 0) {
522               allow_ftp = TRUE;
523               break;
524             }
525           }
526         }
527 
528         if (allow_ftp == FALSE) {
529           pr_log_debug(DEBUG0, "%s", "ftp protocol denied by Protocols config");
530           pr_response_send(R_530, "%s", _("Login incorrect."));
531           pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
532             "Denied by Protocols setting");
533         }
534       }
535     }
536   }
537 
538   user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
539 
540   /* Count up various quantities in the scoreboard, checking them against
541    * the Max* limits to see if the session should be barred from going
542    * any further.
543    */
544   auth_count_scoreboard(cmd, session.user);
545 
546   /* Check for dynamic configuration.  This check needs to be after the
547    * setting of any possible anon_config, as that context may be allowed
548    * or denied .ftpaccess-parsing separately from the containing server.
549    */
550   if (pr_fsio_stat(session.cwd, &st) != -1) {
551     build_dyn_config(cmd->tmp_pool, session.cwd, &st, TRUE);
552   }
553 
554   have_user_timeout = have_group_timeout = have_class_timeout =
555     have_all_timeout = FALSE;
556 
557   c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutSession", FALSE);
558   while (c != NULL) {
559     pr_signals_handle();
560 
561     if (c->argc == 3) {
562       if (strncmp(c->argv[1], "user", 5) == 0) {
563         if (pr_expr_eval_user_or((char **) &c->argv[2]) == TRUE) {
564 
565           if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
566 
567             /* Set the context precedence. */
568             ctxt_precedence = *((unsigned int *) c->argv[1]);
569 
570             TimeoutSession = *((int *) c->argv[0]);
571 
572             have_group_timeout = have_class_timeout = have_all_timeout = FALSE;
573             have_user_timeout = TRUE;
574           }
575         }
576 
577       } else if (strncmp(c->argv[1], "group", 6) == 0) {
578         if (pr_expr_eval_group_and((char **) &c->argv[2]) == TRUE) {
579 
580           if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
581 
582             /* Set the context precedence. */
583             ctxt_precedence = *((unsigned int *) c->argv[1]);
584 
585             TimeoutSession = *((int *) c->argv[0]);
586 
587             have_user_timeout = have_class_timeout = have_all_timeout = FALSE;
588             have_group_timeout = TRUE;
589           }
590         }
591 
592       } else if (strncmp(c->argv[1], "class", 6) == 0) {
593         if (session.conn_class != NULL &&
594             strcmp(session.conn_class->cls_name, c->argv[2]) == 0) {
595 
596           if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
597 
598             /* Set the context precedence. */
599             ctxt_precedence = *((unsigned int *) c->argv[1]);
600 
601             TimeoutSession = *((int *) c->argv[0]);
602 
603             have_user_timeout = have_group_timeout = have_all_timeout = FALSE;
604             have_class_timeout = TRUE;
605           }
606         }
607       }
608 
609     } else {
610 
611       if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
612 
613         /* Set the context precedence. */
614         ctxt_precedence = *((unsigned int *) c->argv[1]);
615 
616         TimeoutSession = *((int *) c->argv[0]);
617 
618         have_user_timeout = have_group_timeout = have_class_timeout = FALSE;
619         have_all_timeout = TRUE;
620       }
621     }
622 
623     c = find_config_next(c, c->next, CONF_PARAM, "TimeoutSession", FALSE);
624   }
625 
626   /* If configured, start a session timer.  The timer ID value for
627    * session timers will not be #defined, as I think that is a bad approach.
628    * A better mechanism would be to use the random timer ID generation, and
629    * store the returned ID in order to later remove the timer.
630    */
631 
632   if (have_user_timeout || have_group_timeout ||
633       have_class_timeout || have_all_timeout) {
634     pr_log_debug(DEBUG4, "setting TimeoutSession of %d seconds for current %s",
635       TimeoutSession,
636       have_user_timeout ? "user" : have_group_timeout ? "group" :
637       have_class_timeout ? "class" : "all");
638     pr_timer_add(TimeoutSession, PR_TIMER_SESSION, &auth_module,
639       auth_session_timeout_cb, "TimeoutSession");
640   }
641 
642   /* Handle a DisplayLogin file. */
643   if (displaylogin_fh != NULL) {
644     if (!(session.sf_flags & SF_ANON)) {
645       if (pr_display_fh(displaylogin_fh, NULL, auth_pass_resp_code, 0) < 0) {
646         pr_log_debug(DEBUG6, "unable to display DisplayLogin file '%s': %s",
647           displaylogin_fh->fh_path, strerror(errno));
648       }
649 
650       pr_fsio_close(displaylogin_fh);
651       displaylogin_fh = NULL;
652 
653     } else {
654       /* We're an <Anonymous> login, but there was a previous DisplayLogin
655        * configured which was picked up earlier.  Close that filehandle,
656        * and look for a new one.
657        */
658       char *displaylogin;
659 
660       pr_fsio_close(displaylogin_fh);
661       displaylogin_fh = NULL;
662 
663       displaylogin = get_param_ptr(TOPLEVEL_CONF, "DisplayLogin", FALSE);
664       if (displaylogin != NULL) {
665         if (pr_display_file(displaylogin, NULL, auth_pass_resp_code, 0) < 0) {
666           pr_log_debug(DEBUG6, "unable to display DisplayLogin file '%s': %s",
667             displaylogin, strerror(errno));
668         }
669       }
670     }
671 
672   } else {
673     char *displaylogin;
674 
675     displaylogin = get_param_ptr(TOPLEVEL_CONF, "DisplayLogin", FALSE);
676     if (displaylogin != NULL) {
677       if (pr_display_file(displaylogin, NULL, auth_pass_resp_code, 0) < 0) {
678         pr_log_debug(DEBUG6, "unable to display DisplayLogin file '%s': %s",
679           displaylogin, strerror(errno));
680       }
681     }
682   }
683 
684   grantmsg = get_param_ptr(TOPLEVEL_CONF, "AccessGrantMsg", FALSE);
685   if (grantmsg == NULL) {
686     /* Append the final greeting lines. */
687     if (session.sf_flags & SF_ANON) {
688       pr_response_add(auth_pass_resp_code, "%s",
689         _("Anonymous access granted, restrictions apply"));
690 
691     } else {
692       pr_response_add(auth_pass_resp_code, _("User %s logged in"), user);
693     }
694 
695   } else {
696      /* Handle any AccessGrantMsg directive. */
697      grantmsg = sreplace(cmd->tmp_pool, grantmsg, "%u", user, NULL);
698      pr_response_add(auth_pass_resp_code, "%s", grantmsg);
699   }
700 
701   login_succeeded(cmd->tmp_pool, user);
702 
703   /* Should we give up root privs completely here? */
704   c = find_config(main_server->conf, CONF_PARAM, "RootRevoke", FALSE);
705   if (c != NULL) {
706     root_revoke = *((int *) c->argv[0]);
707 
708     if (root_revoke == FALSE) {
709       pr_log_debug(DEBUG8, "retaining root privileges per RootRevoke setting");
710     }
711 
712   } else {
713     /* Do a recursive look for any UserOwner directives; honoring that
714      * configuration also requires root privs.
715      */
716     c = find_config(main_server->conf, CONF_PARAM, "UserOwner", TRUE);
717     if (c != NULL) {
718       pr_log_debug(DEBUG9, "retaining root privileges per UserOwner setting");
719       root_revoke = FALSE;
720     }
721   }
722 
723   if (root_revoke) {
724     pr_signals_block();
725     PRIVS_ROOT
726     PRIVS_REVOKE
727     pr_signals_unblock();
728 
729     /* Disable future attempts at UID/GID manipulation. */
730     session.disable_id_switching = TRUE;
731 
732     pr_log_debug(DEBUG2, "RootRevoke in effect, dropped root privs");
733   }
734 
735   c = find_config(TOPLEVEL_CONF, CONF_PARAM, "AnonAllowRobots", FALSE);
736   if (c != NULL) {
737     auth_anon_allow_robots = *((int *) c->argv[0]);
738   }
739 
740   return PR_DECLINED(cmd);
741 }
742 
743 /* Determine any applicable chdirs. */
get_default_chdir(pool * p,xaset_t * conf)744 static const char *get_default_chdir(pool *p, xaset_t *conf) {
745   config_rec *c;
746   const char *dir = NULL;
747 
748   c = find_config(conf, CONF_PARAM, "DefaultChdir", FALSE);
749   while (c != NULL) {
750     int res;
751 
752     pr_signals_handle();
753 
754     /* Check the groups acl */
755     if (c->argc < 2) {
756       dir = c->argv[0];
757       break;
758     }
759 
760     res = pr_expr_eval_group_and(((char **) c->argv)+1);
761     if (res) {
762       dir = c->argv[0];
763       break;
764     }
765 
766     c = find_config_next(c, c->next, CONF_PARAM, "DefaultChdir", FALSE);
767   }
768 
769   /* If the directory is relative, concatenate w/ session.cwd. */
770   if (dir != NULL &&
771       *dir != '/' &&
772       *dir != '~') {
773     dir = pdircat(p, session.cwd, dir, NULL);
774   }
775 
776   /* Check for any expandable variables. */
777   if (dir != NULL) {
778     dir = path_subst_uservar(p, &dir);
779   }
780 
781   return dir;
782 }
783 
is_symlink_path(pool * p,const char * path,size_t pathlen)784 static int is_symlink_path(pool *p, const char *path, size_t pathlen) {
785   int res, xerrno = 0;
786   struct stat st;
787   char *ptr;
788 
789   if (pathlen == 0) {
790     return 0;
791   }
792 
793   pr_fs_clear_cache2(path);
794   res = pr_fsio_lstat(path, &st);
795   if (res < 0) {
796     xerrno = errno;
797 
798     pr_log_pri(PR_LOG_WARNING, "error: unable to check %s: %s", path,
799       strerror(xerrno));
800 
801     errno = xerrno;
802     return -1;
803   }
804 
805   if (S_ISLNK(st.st_mode)) {
806     errno = EPERM;
807     return -1;
808   }
809 
810   /* To handle the case where a component further up the path might be a
811    * symlink (which lstat(2) will NOT handle), we walk the path backwards,
812    * calling ourselves recursively.
813    */
814 
815   ptr = strrchr(path, '/');
816   if (ptr != NULL) {
817     char *new_path;
818     size_t new_pathlen;
819 
820     pr_signals_handle();
821 
822     new_pathlen = ptr - path;
823 
824     /* Make sure our pointer actually changed position. */
825     if (new_pathlen == pathlen) {
826       return 0;
827     }
828 
829     new_path = pstrndup(p, path, new_pathlen);
830 
831     pr_log_debug(DEBUG10,
832       "AllowChrootSymlink: path '%s' not a symlink, checking '%s'", path,
833       new_path);
834     res = is_symlink_path(p, new_path, new_pathlen);
835     if (res < 0) {
836       return -1;
837     }
838   }
839 
840   return 0;
841 }
842 
843 /* Determine if the user (non-anon) needs a default root dir other than /. */
get_default_root(pool * p,int allow_symlinks,const char ** root)844 static int get_default_root(pool *p, int allow_symlinks, const char **root) {
845   config_rec *c = NULL;
846   const char *dir = NULL;
847   int res;
848 
849   c = find_config(main_server->conf, CONF_PARAM, "DefaultRoot", FALSE);
850   while (c != NULL) {
851     pr_signals_handle();
852 
853     /* Check the groups acl */
854     if (c->argc < 2) {
855       dir = c->argv[0];
856       break;
857     }
858 
859     res = pr_expr_eval_group_and(((char **) c->argv)+1);
860     if (res) {
861       dir = c->argv[0];
862       break;
863     }
864 
865     c = find_config_next(c, c->next, CONF_PARAM, "DefaultRoot", FALSE);
866   }
867 
868   if (dir != NULL) {
869     const char *new_dir;
870 
871     /* Check for any expandable variables. */
872     new_dir = path_subst_uservar(p, &dir);
873     if (new_dir != NULL) {
874       dir = new_dir;
875     }
876 
877     if (strncmp(dir, "/", 2) == 0) {
878       dir = NULL;
879 
880     } else {
881       char *realdir;
882       int xerrno = 0;
883 
884       if (allow_symlinks == FALSE) {
885         char *path, target_path[PR_TUNABLE_PATH_MAX + 1];
886         size_t pathlen;
887 
888         /* First, deal with any possible interpolation.  dir_realpath() will
889          * do this for us, but dir_realpath() ALSO automatically follows
890          * symlinks, which is what we do NOT want to do here.
891          */
892 
893         path = pstrdup(p, dir);
894         if (*path != '/') {
895           if (*path == '~') {
896             if (pr_fs_interpolate(dir, target_path,
897                 sizeof(target_path)-1) < 0) {
898               return -1;
899             }
900 
901             path = target_path;
902           }
903         }
904 
905         /* Note: lstat(2) is sensitive to the presence of a trailing slash on
906          * the path, particularly in the case of a symlink to a directory.
907          * Thus to get the correct test, we need to remove any trailing slash
908          * that might be present.  Subtle.
909          */
910         pathlen = strlen(path);
911         if (pathlen > 1 &&
912             path[pathlen-1] == '/') {
913           path[pathlen-1] = '\0';
914         }
915 
916         PRIVS_USER
917         res = is_symlink_path(p, path, pathlen);
918         xerrno = errno;
919         PRIVS_RELINQUISH
920 
921         if (res < 0) {
922           if (xerrno == EPERM) {
923             pr_log_pri(PR_LOG_WARNING, "error: DefaultRoot %s is a symlink "
924               "(denied by AllowChrootSymlinks config)", path);
925           }
926 
927           errno = EPERM;
928           return -1;
929         }
930       }
931 
932       /* We need to be the final user here so that if the user has their home
933        * directory with a mode the user proftpd is running (i.e. the User
934        * directive) as can not traverse down, we can still have the default
935        * root.
936        */
937 
938       pr_fs_clear_cache2(dir);
939 
940       PRIVS_USER
941       realdir = dir_realpath(p, dir);
942       xerrno = errno;
943       PRIVS_RELINQUISH
944 
945       if (realdir) {
946         dir = realdir;
947 
948       } else {
949         /* Try to provide a more informative message. */
950         char interp_dir[PR_TUNABLE_PATH_MAX + 1];
951 
952         memset(interp_dir, '\0', sizeof(interp_dir));
953         (void) pr_fs_interpolate(dir, interp_dir, sizeof(interp_dir)-1);
954 
955         pr_log_pri(PR_LOG_NOTICE,
956           "notice: unable to use DefaultRoot '%s' [resolved to '%s']: %s",
957           dir, interp_dir, strerror(xerrno));
958 
959         errno = xerrno;
960       }
961     }
962   }
963 
964   *root = dir;
965   return 0;
966 }
967 
passwd_dup(pool * p,struct passwd * pw)968 static struct passwd *passwd_dup(pool *p, struct passwd *pw) {
969   struct passwd *npw;
970 
971   npw = pcalloc(p, sizeof(struct passwd));
972 
973   npw->pw_name = pstrdup(p, pw->pw_name);
974   npw->pw_passwd = pstrdup(p, pw->pw_passwd);
975   npw->pw_uid = pw->pw_uid;
976   npw->pw_gid = pw->pw_gid;
977   npw->pw_gecos = pstrdup(p, pw->pw_gecos);
978   npw->pw_dir = pstrdup(p, pw->pw_dir);
979   npw->pw_shell = pstrdup(p, pw->pw_shell);
980 
981   return npw;
982 }
983 
ensure_open_passwd(pool * p)984 static void ensure_open_passwd(pool *p) {
985   /* Make sure pass/group is open. */
986   pr_auth_setpwent(p);
987   pr_auth_setgrent(p);
988 
989   /* On some unices the following is necessary to ensure the files
990    * are open (BSDI 3.1)
991    */
992   pr_auth_getpwent(p);
993   pr_auth_getgrent(p);
994 
995   /* Per Debian bug report:
996    *   https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=717235
997    * we might want to do another set{pw,gr}ent(), to play better with
998    * some NSS modules.
999    */
1000   pr_auth_setpwent(p);
1001   pr_auth_setgrent(p);
1002 }
1003 
1004 /* Next function (the biggie) handles all authentication, setting
1005  * up chroot() jail, etc.
1006  */
setup_env(pool * p,cmd_rec * cmd,const char * user,char * pass)1007 static int setup_env(pool *p, cmd_rec *cmd, const char *user, char *pass) {
1008   struct passwd *pw;
1009   config_rec *c, *tmpc;
1010   const char *defchdir = NULL, *defroot = NULL, *origuser, *sess_ttyname;
1011   char *ourname = NULL, *anonname = NULL, *anongroup = NULL, *ugroup = NULL;
1012   char *xferlog = NULL;
1013   int aclp, i, res = 0, allow_chroot_symlinks = TRUE, showsymlinks;
1014   unsigned char *wtmp_log = NULL, *anon_require_passwd = NULL;
1015 
1016   /********************* Authenticate the user here *********************/
1017 
1018   session.hide_password = TRUE;
1019 
1020   origuser = user;
1021   c = pr_auth_get_anon_config(p, &user, &ourname, &anonname);
1022   if (c != NULL) {
1023     pr_trace_msg("auth", 13,
1024       "found <Anonymous> config: login user = %s, config user = %s, "
1025       "anon name = %s", user != NULL ? user : "(null)",
1026       ourname != NULL ? ourname : "(null)",
1027       anonname != NULL ? anonname : "(null)");
1028     session.anon_config = c;
1029   }
1030 
1031   if (user == NULL) {
1032     pr_log_auth(PR_LOG_NOTICE, "USER %s: user is not a UserAlias from %s [%s] "
1033       "to %s:%i", origuser, session.c->remote_name,
1034       pr_netaddr_get_ipstr(session.c->remote_addr),
1035       pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port);
1036     goto auth_failure;
1037   }
1038 
1039   pw = pr_auth_getpwnam(p, user);
1040   if (pw == NULL &&
1041       c != NULL &&
1042       ourname != NULL) {
1043     /* If the client is authenticating using an alias (e.g. "AuthAliasOnly on"),
1044      * then we need to try checking using the real username, too (Bug#4255).
1045      */
1046     pr_trace_msg("auth", 16,
1047       "no user entry found for <Anonymous> alias '%s', using '%s'", user,
1048       ourname);
1049     pw = pr_auth_getpwnam(p, ourname);
1050   }
1051 
1052   if (pw == NULL) {
1053     int auth_code = PR_AUTH_NOPWD;
1054 
1055     pr_log_auth(PR_LOG_NOTICE,
1056       "USER %s: no such user found from %s [%s] to %s:%i",
1057       user, session.c->remote_name,
1058       pr_netaddr_get_ipstr(session.c->remote_addr),
1059       pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port);
1060     pr_event_generate("mod_auth.authentication-code", &auth_code);
1061 
1062     goto auth_failure;
1063   }
1064 
1065   /* Security: other functions perform pw lookups, thus we need to make
1066    * a local copy of the user just looked up.
1067    */
1068   pw = passwd_dup(p, pw);
1069 
1070   if (pw->pw_uid == PR_ROOT_UID) {
1071     unsigned char *root_allow = NULL;
1072 
1073     pr_event_generate("mod_auth.root-login", NULL);
1074 
1075     /* If RootLogin is set to true, we allow this... even though we
1076      * still log a warning. :)
1077      */
1078     if ((root_allow = get_param_ptr(c ? c->subset : main_server->conf,
1079         "RootLogin", FALSE)) == NULL || *root_allow != TRUE) {
1080       if (pass) {
1081         pr_memscrub(pass, strlen(pass));
1082       }
1083 
1084       pr_log_auth(PR_LOG_NOTICE, "SECURITY VIOLATION: Root login attempted");
1085       return 0;
1086     }
1087   }
1088 
1089   session.user = pstrdup(p, pw->pw_name);
1090   session.group = pstrdup(p, pr_auth_gid2name(p, pw->pw_gid));
1091 
1092   /* Set the login_uid and login_uid */
1093   session.login_uid = pw->pw_uid;
1094   session.login_gid = pw->pw_gid;
1095 
1096   /* Check for any expandable variables in session.cwd. */
1097   pw->pw_dir = (char *) path_subst_uservar(p, (const char **) &pw->pw_dir);
1098 
1099   /* Before we check for supplemental groups, check to see if the locally
1100    * resolved name of the user, returned via auth_getpwnam(), is different
1101    * from the USER argument sent by the client.  The name can change, since
1102    * auth modules can play all sorts of neat tricks on us.
1103    *
1104    * If the names differ, assume that any cached data in the session.gids
1105    * and session.groups lists are stale, and clear them out.
1106    */
1107   if (strcmp(pw->pw_name, user) != 0) {
1108     pr_trace_msg("auth", 10, "local user name '%s' differs from client-sent "
1109       "user name '%s', clearing cached group data", pw->pw_name, user);
1110     session.gids = NULL;
1111     session.groups = NULL;
1112   }
1113 
1114   if (!session.gids &&
1115       !session.groups) {
1116     /* Get the supplemental groups.  Note that we only look up the
1117      * supplemental group credentials if we have not cached the group
1118      * credentials before, in session.gids and session.groups.
1119      *
1120      * Those credentials may have already been retrieved, as part of the
1121      * pr_auth_get_anon_config() call.
1122      */
1123      res = pr_auth_getgroups(p, pw->pw_name, &session.gids, &session.groups);
1124      if (res < 1) {
1125        pr_log_debug(DEBUG5, "no supplemental groups found for user '%s'",
1126          pw->pw_name);
1127      }
1128   }
1129 
1130   tmpc = find_config(main_server->conf, CONF_PARAM, "AllowChrootSymlinks",
1131     FALSE);
1132   if (tmpc != NULL) {
1133     allow_chroot_symlinks = *((int *) tmpc->argv[0]);
1134   }
1135 
1136   /* If c != NULL from this point on, we have an anonymous login */
1137   aclp = login_check_limits(main_server->conf, FALSE, TRUE, &i);
1138 
1139   if (c != NULL) {
1140     anongroup = get_param_ptr(c->subset, "GroupName", FALSE);
1141     if (anongroup == NULL) {
1142       anongroup = get_param_ptr(main_server->conf, "GroupName",FALSE);
1143     }
1144 
1145 #ifdef PR_USE_REGEX
1146     /* Check for configured AnonRejectPasswords regex here, and fail the login
1147      * if the given password matches the regex.
1148      */
1149     tmpc = find_config(c->subset, CONF_PARAM, "AnonRejectPasswords", FALSE);
1150     if (tmpc != NULL) {
1151       int re_notmatch;
1152       pr_regex_t *pw_regex;
1153 
1154       pw_regex = (pr_regex_t *) tmpc->argv[0];
1155       re_notmatch = *((int *) tmpc->argv[1]);
1156 
1157       if (pw_regex != NULL &&
1158           pass != NULL) {
1159         int re_res;
1160 
1161         re_res = pr_regexp_exec(pw_regex, pass, 0, NULL, 0, 0, 0);
1162         if (re_res == 0 ||
1163             (re_res != 0 && re_notmatch == TRUE)) {
1164           char errstr[200] = {'\0'};
1165 
1166           pr_regexp_error(re_res, pw_regex, errstr, sizeof(errstr));
1167           pr_log_auth(PR_LOG_NOTICE,
1168             "ANON %s: AnonRejectPasswords denies login", origuser);
1169 
1170           pr_event_generate("mod_auth.anon-reject-passwords", session.c);
1171           goto auth_failure;
1172         }
1173       }
1174     }
1175 #endif
1176 
1177     if (!login_check_limits(c->subset, FALSE, TRUE, &i) || (!aclp && !i) ){
1178       pr_log_auth(PR_LOG_NOTICE, "ANON %s (Login failed): Limit access denies "
1179         "login", origuser);
1180       goto auth_failure;
1181     }
1182   }
1183 
1184   if (c == NULL &&
1185       aclp == 0) {
1186     pr_log_auth(PR_LOG_NOTICE,
1187       "USER %s (Login failed): Limit access denies login", origuser);
1188     goto auth_failure;
1189   }
1190 
1191   if (c != NULL) {
1192     anon_require_passwd = get_param_ptr(c->subset, "AnonRequirePassword",
1193       FALSE);
1194   }
1195 
1196   if (c == NULL ||
1197       (anon_require_passwd != NULL &&
1198        *anon_require_passwd == TRUE)) {
1199     int auth_code;
1200     const char *user_name = user;
1201 
1202     if (c != NULL &&
1203         origuser != NULL &&
1204         strcasecmp(user, origuser) != 0) {
1205       unsigned char *auth_using_alias;
1206 
1207       auth_using_alias = get_param_ptr(c->subset, "AuthUsingAlias", FALSE);
1208 
1209       /* If 'AuthUsingAlias' set and we're logging in under an alias,
1210        * then auth using that alias.
1211        */
1212       if (auth_using_alias &&
1213           *auth_using_alias == TRUE) {
1214         user_name = origuser;
1215         pr_log_auth(PR_LOG_INFO,
1216           "ANON AUTH: User %s, authenticating using alias %s", user,
1217           user_name);
1218       }
1219     }
1220 
1221     /* It is possible for the user to have already been authenticated during
1222      * the handling of the USER command, as by an RFC2228 mechanism.  If
1223      * that had happened, we won't need to call do_auth() here.
1224      */
1225     if (!authenticated_without_pass) {
1226       auth_code = do_auth(p, c ? c->subset : main_server->conf, user_name,
1227         pass);
1228 
1229     } else {
1230       auth_code = PR_AUTH_OK_NO_PASS;
1231     }
1232 
1233     pr_event_generate("mod_auth.authentication-code", &auth_code);
1234 
1235     if (pass != NULL) {
1236       pr_memscrub(pass, strlen(pass));
1237     }
1238 
1239     if (session.auth_mech != NULL)
1240       pr_log_debug(DEBUG2, "user '%s' authenticated by %s", user,
1241         session.auth_mech);
1242 
1243     switch (auth_code) {
1244       case PR_AUTH_OK_NO_PASS:
1245         auth_pass_resp_code = R_232;
1246         break;
1247 
1248       case PR_AUTH_OK:
1249         auth_pass_resp_code = R_230;
1250         break;
1251 
1252       case PR_AUTH_NOPWD:
1253         pr_log_auth(PR_LOG_NOTICE,
1254           "USER %s (Login failed): No such user found", user);
1255         goto auth_failure;
1256 
1257       case PR_AUTH_BADPWD:
1258         pr_log_auth(PR_LOG_NOTICE,
1259           "USER %s (Login failed): Incorrect password", origuser);
1260         goto auth_failure;
1261 
1262       case PR_AUTH_AGEPWD:
1263         pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): Password expired",
1264           user);
1265         goto auth_failure;
1266 
1267       case PR_AUTH_DISABLEDPWD:
1268         pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): Account disabled",
1269           user);
1270         goto auth_failure;
1271 
1272       case PR_AUTH_CRED_INSUFFICIENT:
1273         pr_log_auth(PR_LOG_NOTICE,
1274           "USER %s (Login failed): Insufficient credentials", user);
1275         goto auth_failure;
1276 
1277       case PR_AUTH_CRED_UNAVAIL:
1278         pr_log_auth(PR_LOG_NOTICE,
1279           "USER %s (Login failed): Unavailable credentials", user);
1280         goto auth_failure;
1281 
1282       case PR_AUTH_CRED_ERROR:
1283         pr_log_auth(PR_LOG_NOTICE,
1284           "USER %s (Login failed): Failure setting credentials", user);
1285         goto auth_failure;
1286 
1287       case PR_AUTH_INFO_UNAVAIL:
1288         pr_log_auth(PR_LOG_NOTICE,
1289           "USER %s (Login failed): Unavailable authentication service", user);
1290         goto auth_failure;
1291 
1292       case PR_AUTH_MAX_ATTEMPTS_EXCEEDED:
1293         pr_log_auth(PR_LOG_NOTICE,
1294           "USER %s (Login failed): Max authentication service attempts reached",
1295           user);
1296         goto auth_failure;
1297 
1298       case PR_AUTH_INIT_ERROR:
1299         pr_log_auth(PR_LOG_NOTICE,
1300           "USER %s (Login failed): Failed initializing authentication service",
1301           user);
1302         goto auth_failure;
1303 
1304       case PR_AUTH_NEW_TOKEN_REQUIRED:
1305         pr_log_auth(PR_LOG_NOTICE,
1306           "USER %s (Login failed): New authentication token required", user);
1307         goto auth_failure;
1308 
1309       default:
1310         break;
1311     };
1312 
1313     /* Catch the case where we forgot to handle a bad auth code above. */
1314     if (auth_code < 0)
1315       goto auth_failure;
1316 
1317     if (pw->pw_uid == PR_ROOT_UID) {
1318       pr_log_auth(PR_LOG_WARNING, "ROOT FTP login successful");
1319     }
1320 
1321   } else if (c && (!anon_require_passwd || *anon_require_passwd == FALSE)) {
1322     session.hide_password = FALSE;
1323   }
1324 
1325   pr_auth_setgrent(p);
1326 
1327   res = pr_auth_is_valid_shell(c ? c->subset : main_server->conf,
1328     pw->pw_shell);
1329   if (res == FALSE) {
1330     pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): Invalid shell: '%s'",
1331       user, pw->pw_shell);
1332     goto auth_failure;
1333   }
1334 
1335   res = pr_auth_banned_by_ftpusers(c ? c->subset : main_server->conf,
1336     pw->pw_name);
1337   if (res == TRUE) {
1338     pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User in "
1339       PR_FTPUSERS_PATH, user);
1340     goto auth_failure;
1341   }
1342 
1343   if (c) {
1344     struct group *grp = NULL;
1345     unsigned char *add_userdir = NULL;
1346     const char *u;
1347     char *chroot_dir;
1348 
1349     u = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
1350     add_userdir = get_param_ptr(c->subset, "UserDirRoot", FALSE);
1351 
1352     /* If resolving an <Anonymous> user, make sure that user's groups
1353      * are set properly for the check of the home directory path (which
1354      * depend on those supplemental group memberships).  Additionally,
1355      * temporarily switch to the new user's uid.
1356      */
1357 
1358     pr_signals_block();
1359 
1360     PRIVS_ROOT
1361     res = set_groups(p, pw->pw_gid, session.gids);
1362     if (res < 0) {
1363       if (errno != ENOSYS) {
1364         pr_log_pri(PR_LOG_WARNING, "error: unable to set groups: %s",
1365           strerror(errno));
1366       }
1367     }
1368 
1369 #ifndef PR_DEVEL_COREDUMP
1370 # ifdef __hpux
1371     if (setresuid(0, 0, 0) < 0) {
1372       pr_log_pri(PR_LOG_ERR, "unable to setresuid(): %s", strerror(errno));
1373     }
1374 
1375     if (setresgid(0, 0, 0) < 0) {
1376       pr_log_pri(PR_LOG_ERR, "unable to setresgid(): %s", strerror(errno));
1377     }
1378 # else
1379     if (setuid(PR_ROOT_UID) < 0) {
1380       pr_log_pri(PR_LOG_ERR, "unable to setuid(): %s", strerror(errno));
1381     }
1382 
1383     if (setgid(PR_ROOT_GID) < 0) {
1384       pr_log_pri(PR_LOG_ERR, "unable to setgid(): %s", strerror(errno));
1385     }
1386 # endif /* __hpux */
1387 #endif /* PR_DEVEL_COREDUMP */
1388 
1389     PRIVS_SETUP(pw->pw_uid, pw->pw_gid)
1390 
1391     if ((add_userdir && *add_userdir == TRUE) &&
1392         strcmp(u, user) != 0) {
1393       chroot_dir = pdircat(p, c->name, u, NULL);
1394 
1395     } else {
1396       chroot_dir = c->name;
1397     }
1398 
1399     if (allow_chroot_symlinks == FALSE) {
1400       char *chroot_path, target_path[PR_TUNABLE_PATH_MAX+1];
1401       struct stat st;
1402 
1403       chroot_path = chroot_dir;
1404       if (chroot_path[0] != '/') {
1405         if (chroot_path[0] == '~') {
1406           if (pr_fs_interpolate(chroot_path, target_path,
1407               sizeof(target_path)-1) == 0) {
1408             chroot_path = target_path;
1409 
1410           } else {
1411             chroot_path = NULL;
1412           }
1413         }
1414       }
1415 
1416       if (chroot_path != NULL) {
1417         size_t chroot_pathlen;
1418 
1419         /* Note: lstat(2) is sensitive to the presence of a trailing slash on
1420          * the path, particularly in the case of a symlink to a directory.
1421          * Thus to get the correct test, we need to remove any trailing slash
1422          * that might be present.  Subtle.
1423          */
1424         chroot_pathlen = strlen(chroot_path);
1425         if (chroot_pathlen > 1 &&
1426             chroot_path[chroot_pathlen-1] == '/') {
1427           chroot_path[chroot_pathlen-1] = '\0';
1428         }
1429 
1430         pr_fs_clear_cache2(chroot_path);
1431         res = pr_fsio_lstat(chroot_path, &st);
1432         if (res < 0) {
1433           int xerrno = errno;
1434 
1435           pr_log_pri(PR_LOG_WARNING, "error: unable to check %s: %s",
1436             chroot_path, strerror(xerrno));
1437 
1438           errno = xerrno;
1439           chroot_path = NULL;
1440 
1441         } else {
1442           if (S_ISLNK(st.st_mode)) {
1443             pr_log_pri(PR_LOG_WARNING,
1444               "error: <Anonymous %s> is a symlink (denied by "
1445               "AllowChrootSymlinks config)", chroot_path);
1446             errno = EPERM;
1447             chroot_path = NULL;
1448           }
1449         }
1450       }
1451 
1452       if (chroot_path != NULL) {
1453         session.chroot_path = dir_realpath(p, chroot_dir);
1454 
1455       } else {
1456         session.chroot_path = NULL;
1457       }
1458 
1459       if (session.chroot_path == NULL) {
1460         pr_log_debug(DEBUG8, "error resolving '%s': %s", chroot_dir,
1461           strerror(errno));
1462       }
1463 
1464     } else {
1465       session.chroot_path = dir_realpath(p, chroot_dir);
1466       if (session.chroot_path == NULL) {
1467         pr_log_debug(DEBUG8, "error resolving '%s': %s", chroot_dir,
1468           strerror(errno));
1469       }
1470     }
1471 
1472     if (session.chroot_path &&
1473         pr_fsio_access(session.chroot_path, X_OK, session.uid,
1474           session.gid, session.gids) != 0) {
1475       session.chroot_path = NULL;
1476 
1477     } else {
1478       session.chroot_path = pstrdup(session.pool, session.chroot_path);
1479     }
1480 
1481     /* Return all privileges back to that of the daemon, for now. */
1482     PRIVS_ROOT
1483     res = set_groups(p, daemon_gid, daemon_gids);
1484     if (res < 0) {
1485       if (errno != ENOSYS) {
1486         pr_log_pri(PR_LOG_ERR, "error: unable to set groups: %s",
1487           strerror(errno));
1488       }
1489     }
1490 
1491 #ifndef PR_DEVEL_COREDUMP
1492 # ifdef __hpux
1493     if (setresuid(0, 0, 0) < 0) {
1494       pr_log_pri(PR_LOG_ERR, "unable to setresuid(): %s", strerror(errno));
1495     }
1496 
1497     if (setresgid(0, 0, 0) < 0) {
1498       pr_log_pri(PR_LOG_ERR, "unable to setresgid(): %s", strerror(errno));
1499     }
1500 # else
1501     if (setuid(PR_ROOT_UID) < 0) {
1502       pr_log_pri(PR_LOG_ERR, "unable to setuid(): %s", strerror(errno));
1503     }
1504 
1505     if (setgid(PR_ROOT_GID) < 0) {
1506       pr_log_pri(PR_LOG_ERR, "unable to setgid(): %s", strerror(errno));
1507     }
1508 # endif /* __hpux */
1509 #endif /* PR_DEVEL_COREDUMP */
1510 
1511     PRIVS_SETUP(daemon_uid, daemon_gid)
1512 
1513     pr_signals_unblock();
1514 
1515     /* Sanity check, make sure we have daemon_uid and daemon_gid back */
1516 #ifdef HAVE_GETEUID
1517     if (getegid() != daemon_gid ||
1518         geteuid() != daemon_uid) {
1519 
1520       PRIVS_RELINQUISH
1521 
1522       pr_log_pri(PR_LOG_WARNING,
1523         "switching IDs from user %s back to daemon uid/gid failed: %s",
1524         session.user, strerror(errno));
1525       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_BY_APPLICATION,
1526         NULL);
1527     }
1528 #endif /* HAVE_GETEUID */
1529 
1530     if (anon_require_passwd &&
1531         *anon_require_passwd == TRUE) {
1532       session.anon_user = pstrdup(session.pool, origuser);
1533 
1534     } else {
1535       session.anon_user = pstrdup(session.pool, pass);
1536     }
1537 
1538     if (!session.chroot_path) {
1539       pr_log_pri(PR_LOG_NOTICE, "%s: Directory %s is not accessible",
1540         session.user, c->name);
1541       pr_response_add_err(R_530, _("Unable to set anonymous privileges."));
1542       goto auth_failure;
1543     }
1544 
1545     sstrncpy(session.cwd, "/", sizeof(session.cwd));
1546     xferlog = get_param_ptr(c->subset, "TransferLog", FALSE);
1547 
1548     if (anongroup) {
1549       grp = pr_auth_getgrnam(p, anongroup);
1550       if (grp) {
1551         pw->pw_gid = grp->gr_gid;
1552         session.group = pstrdup(p, grp->gr_name);
1553       }
1554     }
1555 
1556   } else {
1557     struct group *grp;
1558     char *homedir;
1559 
1560     if (ugroup) {
1561       grp = pr_auth_getgrnam(p, ugroup);
1562       if (grp) {
1563         pw->pw_gid = grp->gr_gid;
1564         session.group = pstrdup(p, grp->gr_name);
1565       }
1566     }
1567 
1568     /* Attempt to resolve any possible symlinks. */
1569     PRIVS_USER
1570     homedir = dir_realpath(p, pw->pw_dir);
1571     PRIVS_RELINQUISH
1572 
1573     if (homedir)
1574       sstrncpy(session.cwd, homedir, sizeof(session.cwd));
1575     else
1576       sstrncpy(session.cwd, pw->pw_dir, sizeof(session.cwd));
1577   }
1578 
1579   /* Create the home directory, if need be. */
1580 
1581   if (!c && mkhome) {
1582     if (create_home(p, session.cwd, origuser, pw->pw_uid, pw->pw_gid) < 0) {
1583 
1584       /* NOTE: should this cause the login to fail? */
1585       goto auth_failure;
1586     }
1587   }
1588 
1589   /* Get default chdir (if any) */
1590   defchdir = get_default_chdir(p, (c ? c->subset : main_server->conf));
1591   if (defchdir != NULL) {
1592     sstrncpy(session.cwd, defchdir, sizeof(session.cwd));
1593   }
1594 
1595   /* Check limits again to make sure deny/allow directives still permit
1596    * access.
1597    */
1598 
1599   if (!login_check_limits((c ? c->subset : main_server->conf), FALSE, TRUE,
1600       &i)) {
1601     pr_log_auth(PR_LOG_NOTICE, "%s %s: Limit access denies login",
1602       (c != NULL) ? "ANON" : C_USER, origuser);
1603     goto auth_failure;
1604   }
1605 
1606   /* Perform a directory fixup. */
1607   resolve_deferred_dirs(main_server);
1608   fixup_dirs(main_server, CF_DEFER);
1609 
1610   /* If running under an anonymous context, resolve all <Directory>
1611    * blocks inside it.
1612    */
1613   if (c && c->subset)
1614     resolve_anonymous_dirs(c->subset);
1615 
1616   /* Write the login to wtmp.  This must be done here because we won't
1617    * have access after we give up root.  This can result in falsified
1618    * wtmp entries if an error kicks the user out before we get
1619    * through with the login process.  Oh well.
1620    */
1621 
1622   sess_ttyname = pr_session_get_ttyname(p);
1623 
1624   /* Perform wtmp logging only if not turned off in <Anonymous>
1625    * or the current server
1626    */
1627   if (c)
1628     wtmp_log = get_param_ptr(c->subset, "WtmpLog", FALSE);
1629 
1630   if (wtmp_log == NULL)
1631     wtmp_log = get_param_ptr(main_server->conf, "WtmpLog", FALSE);
1632 
1633   /* As per Bug#3482, we need to disable WtmpLog for FreeBSD 9.0, as
1634    * an interim measure.
1635    *
1636    * The issue is that some platforms update multiple files for a single
1637    * pututxline(3) call; proftpd tries to update those files manually,
1638    * do to chroots (after which a pututxline(3) call will fail).  A proper
1639    * solution requires a separate process, running with the correct
1640    * privileges, which would handle wtmp logging. The proftpd session
1641    * processes would send messages to this logging daemon (via Unix domain
1642    * socket, or FIFO, or TCP socket).
1643    *
1644    * Also note that this hack to disable WtmpLog may need to be extended
1645    * to other platforms in the future.
1646    */
1647 #if defined(HAVE_UTMPX_H) && \
1648     defined(__FreeBSD_version) && __FreeBSD_version >= 900007
1649   if (wtmp_log == NULL ||
1650       *wtmp_log == TRUE) {
1651     wtmp_log = pcalloc(p, sizeof(unsigned char));
1652     *wtmp_log = FALSE;
1653 
1654     pr_log_debug(DEBUG5,
1655       "WtpmLog automatically disabled; see Bug#3482 for details");
1656   }
1657 #endif
1658 
1659   PRIVS_ROOT
1660 
1661   if (wtmp_log == NULL ||
1662       *wtmp_log == TRUE) {
1663     log_wtmp(sess_ttyname, session.user, session.c->remote_name,
1664       session.c->remote_addr);
1665     session.wtmp_log = TRUE;
1666   }
1667 
1668 #ifdef PR_USE_LASTLOG
1669   if (lastlog) {
1670     log_lastlog(pw->pw_uid, session.user, sess_ttyname, session.c->remote_addr);
1671   }
1672 #endif /* PR_USE_LASTLOG */
1673 
1674   /* Open any TransferLogs */
1675   if (!xferlog) {
1676     if (c)
1677       xferlog = get_param_ptr(c->subset, "TransferLog", FALSE);
1678 
1679     if (!xferlog)
1680       xferlog = get_param_ptr(main_server->conf, "TransferLog", FALSE);
1681 
1682     if (!xferlog)
1683       xferlog = PR_XFERLOG_PATH;
1684   }
1685 
1686   if (strcasecmp(xferlog, "NONE") == 0) {
1687     xferlog_open(NULL);
1688 
1689   } else {
1690     xferlog_open(xferlog);
1691   }
1692 
1693   res = set_groups(p, pw->pw_gid, session.gids);
1694   if (res < 0) {
1695     if (errno != ENOSYS) {
1696       pr_log_pri(PR_LOG_ERR, "error: unable to set groups: %s",
1697         strerror(errno));
1698     }
1699   }
1700 
1701   PRIVS_RELINQUISH
1702 
1703   /* Now check to see if the user has an applicable DefaultRoot */
1704   if (c == NULL) {
1705     if (get_default_root(session.pool, allow_chroot_symlinks, &defroot) < 0) {
1706       pr_log_pri(PR_LOG_NOTICE,
1707         "error: unable to determine DefaultRoot directory");
1708       pr_response_send(R_530, _("Login incorrect."));
1709       pr_session_end(0);
1710     }
1711 
1712     ensure_open_passwd(p);
1713 
1714     if (defroot != NULL) {
1715       if (pr_auth_chroot(defroot) == -1) {
1716         pr_log_pri(PR_LOG_NOTICE, "error: unable to set DefaultRoot directory");
1717         pr_response_send(R_530, _("Login incorrect."));
1718         pr_session_end(0);
1719       }
1720 
1721       /* Re-calc the new cwd based on this root dir.  If not applicable
1722        * place the user in / (of defroot)
1723        */
1724 
1725       if (strncmp(session.cwd, defroot, strlen(defroot)) == 0) {
1726         char *newcwd = &session.cwd[strlen(defroot)];
1727 
1728         if (*newcwd == '/')
1729           newcwd++;
1730         session.cwd[0] = '/';
1731         sstrncpy(&session.cwd[1], newcwd, sizeof(session.cwd));
1732       }
1733     }
1734   }
1735 
1736   if (c)
1737     ensure_open_passwd(p);
1738 
1739   if (c &&
1740       pr_auth_chroot(session.chroot_path) == -1) {
1741     pr_log_pri(PR_LOG_NOTICE, "error: unable to set anonymous privileges");
1742     pr_response_send(R_530, _("Login incorrect."));
1743     pr_session_end(0);
1744   }
1745 
1746   /* new in 1.1.x, I gave in and we don't give up root permanently..
1747    * sigh.
1748    */
1749 
1750   PRIVS_ROOT
1751 
1752 #ifndef PR_DEVEL_COREDUMP
1753 # ifdef __hpux
1754     if (setresuid(0, 0, 0) < 0) {
1755       pr_log_pri(PR_LOG_ERR, "unable to setresuid(): %s", strerror(errno));
1756     }
1757 
1758     if (setresgid(0, 0, 0) < 0) {
1759       pr_log_pri(PR_LOG_ERR, "unable to setresgid(): %s", strerror(errno));
1760     }
1761 # else
1762     if (setuid(PR_ROOT_UID) < 0) {
1763       pr_log_pri(PR_LOG_ERR, "unable to setuid(): %s", strerror(errno));
1764     }
1765 
1766     if (setgid(PR_ROOT_GID) < 0) {
1767       pr_log_pri(PR_LOG_ERR, "unable to setgid(): %s", strerror(errno));
1768     }
1769 # endif /* __hpux */
1770 #endif /* PR_DEVEL_COREDUMP */
1771 
1772   PRIVS_SETUP(pw->pw_uid, pw->pw_gid)
1773 
1774 #ifdef HAVE_GETEUID
1775   if (getegid() != pw->pw_gid ||
1776      geteuid() != pw->pw_uid) {
1777 
1778     PRIVS_RELINQUISH
1779     pr_log_pri(PR_LOG_ERR, "error: %s setregid() or setreuid(): %s",
1780       session.user, strerror(errno));
1781     pr_response_send(R_530, _("Login incorrect."));
1782     pr_session_end(0);
1783   }
1784 #endif
1785 
1786   /* If the home directory is NULL or "", reject the login. */
1787   if (pw->pw_dir == NULL ||
1788       strncmp(pw->pw_dir, "", 1) == 0) {
1789     pr_log_pri(PR_LOG_WARNING, "error: user %s home directory is NULL or \"\"",
1790       session.user);
1791     pr_response_send(R_530, _("Login incorrect."));
1792     pr_session_end(0);
1793   }
1794 
1795   {
1796     unsigned char *show_symlinks = get_param_ptr(
1797       c ? c->subset : main_server->conf, "ShowSymlinks", FALSE);
1798 
1799     if (!show_symlinks || *show_symlinks == TRUE)
1800       showsymlinks = TRUE;
1801     else
1802       showsymlinks = FALSE;
1803   }
1804 
1805   /* chdir to the proper directory, do this even if anonymous
1806    * to make sure we aren't outside our chrooted space.
1807    */
1808 
1809   /* Attempt to change to the correct directory -- use session.cwd first.
1810    * This will contain the DefaultChdir directory, if configured...
1811    */
1812   if (pr_fsio_chdir_canon(session.cwd, !showsymlinks) == -1) {
1813 
1814     /* if we've got DefaultRoot or anonymous login, ignore this error
1815      * and chdir to /
1816      */
1817 
1818     if (session.chroot_path != NULL || defroot) {
1819 
1820       pr_log_debug(DEBUG2, "unable to chdir to %s (%s), defaulting to chroot "
1821         "directory %s", session.cwd, strerror(errno),
1822         (session.chroot_path ? session.chroot_path : defroot));
1823 
1824       if (pr_fsio_chdir_canon("/", !showsymlinks) == -1) {
1825         pr_log_pri(PR_LOG_NOTICE, "%s chdir(\"/\") failed: %s", session.user,
1826           strerror(errno));
1827         pr_response_send(R_530, _("Login incorrect."));
1828         pr_session_end(0);
1829       }
1830 
1831     } else if (defchdir) {
1832 
1833       /* If we've got defchdir, failure is ok as well, simply switch to
1834        * user's homedir.
1835        */
1836       pr_log_debug(DEBUG2, "unable to chdir to %s (%s), defaulting to home "
1837         "directory %s", session.cwd, strerror(errno), pw->pw_dir);
1838 
1839       if (pr_fsio_chdir_canon(pw->pw_dir, !showsymlinks) == -1) {
1840         pr_log_pri(PR_LOG_NOTICE, "%s chdir(\"%s\") failed: %s", session.user,
1841           session.cwd, strerror(errno));
1842         pr_response_send(R_530, _("Login incorrect."));
1843         pr_session_end(0);
1844       }
1845 
1846     } else {
1847 
1848       /* Unable to switch to user's real home directory, which is not
1849        * allowed.
1850        */
1851       pr_log_pri(PR_LOG_NOTICE, "%s chdir(\"%s\") failed: %s", session.user,
1852         session.cwd, strerror(errno));
1853       pr_response_send(R_530, _("Login incorrect."));
1854       pr_session_end(0);
1855     }
1856   }
1857 
1858   sstrncpy(session.cwd, pr_fs_getcwd(), sizeof(session.cwd));
1859   sstrncpy(session.vwd, pr_fs_getvwd(), sizeof(session.vwd));
1860 
1861   /* Make sure directory config pointers are set correctly */
1862   dir_check_full(p, cmd, G_NONE, session.cwd, NULL);
1863 
1864   if (c) {
1865     if (!session.hide_password) {
1866       session.proc_prefix = pstrcat(session.pool, session.c->remote_name,
1867         ": anonymous/", pass, NULL);
1868 
1869     } else {
1870       session.proc_prefix = pstrcat(session.pool, session.c->remote_name,
1871         ": anonymous", NULL);
1872     }
1873 
1874     session.sf_flags = SF_ANON;
1875 
1876   } else {
1877     session.proc_prefix = pstrdup(session.pool, session.c->remote_name);
1878     session.sf_flags = 0;
1879   }
1880 
1881   /* While closing the pointer to the password database would avoid any
1882    * potential attempt to hijack this information, it is unfortunately needed
1883    * in a chroot()ed environment.  Otherwise, mappings from UIDs to names,
1884    * among other things, would fail.
1885    */
1886   /* pr_auth_endpwent(p); */
1887 
1888   /* Authentication complete, user logged in, now kill the login
1889    * timer.
1890    */
1891 
1892   /* Update the scoreboard entry */
1893   pr_scoreboard_entry_update(session.pid,
1894     PR_SCORE_USER, session.user,
1895     PR_SCORE_CWD, session.cwd,
1896     NULL);
1897 
1898   pr_session_set_idle();
1899 
1900   pr_timer_remove(PR_TIMER_LOGIN, &auth_module);
1901 
1902   /* These copies are made from the session.pool, instead of the more
1903    * volatile pool used originally, in order that the copied data maintain
1904    * its integrity for the lifetime of the session.
1905    */
1906   session.user = pstrdup(session.pool, session.user);
1907 
1908   if (session.group)
1909     session.group = pstrdup(session.pool, session.group);
1910 
1911   if (session.gids)
1912     session.gids = copy_array(session.pool, session.gids);
1913 
1914   /* session.groups is an array of strings, so we must copy the string data
1915    * as well as the pointers.
1916    */
1917   session.groups = copy_array_str(session.pool, session.groups);
1918 
1919   /* Resolve any deferred-resolution paths in the FS layer */
1920   pr_resolve_fs_map();
1921 
1922   return 1;
1923 
1924 auth_failure:
1925   if (pass)
1926     pr_memscrub(pass, strlen(pass));
1927   session.user = session.group = NULL;
1928   session.gids = session.groups = NULL;
1929   session.wtmp_log = FALSE;
1930   return 0;
1931 }
1932 
1933 /* This function counts the number of connected users. It only fills in the
1934  * Class-based counters and an estimate for the number of clients. The primary
1935  * purpose is to make it so that the %N/%y escapes work in a DisplayConnect
1936  * greeting.  A secondary purpose is to enforce any configured
1937  * MaxConnectionsPerHost limit.
1938  */
auth_scan_scoreboard(void)1939 static int auth_scan_scoreboard(void) {
1940   char *key;
1941   void *v;
1942   config_rec *c = NULL;
1943   pr_scoreboard_entry_t *score = NULL;
1944   unsigned int cur = 0, ccur = 0, hcur = 0;
1945   char curr_server_addr[80] = {'\0'};
1946   const char *client_addr = pr_netaddr_get_ipstr(session.c->remote_addr);
1947 
1948   pr_snprintf(curr_server_addr, sizeof(curr_server_addr), "%s:%d",
1949     pr_netaddr_get_ipstr(session.c->local_addr), main_server->ServerPort);
1950   curr_server_addr[sizeof(curr_server_addr)-1] = '\0';
1951 
1952   /* Determine how many users are currently connected */
1953   if (pr_rewind_scoreboard() < 0) {
1954     pr_log_pri(PR_LOG_NOTICE, "error rewinding scoreboard: %s",
1955       strerror(errno));
1956   }
1957 
1958   while ((score = pr_scoreboard_entry_read()) != NULL) {
1959     pr_signals_handle();
1960 
1961     /* Make sure it matches our current server */
1962     if (strcmp(score->sce_server_addr, curr_server_addr) == 0) {
1963       cur++;
1964 
1965       if (strcmp(score->sce_client_addr, client_addr) == 0)
1966         hcur++;
1967 
1968       /* Only count up authenticated clients, as per the documentation. */
1969       if (strncmp(score->sce_user, "(none)", 7) == 0)
1970         continue;
1971 
1972       /* Note: the class member of the scoreboard entry will never be
1973        * NULL.  At most, it may be the empty string.
1974        */
1975       if (session.conn_class != NULL &&
1976           strcasecmp(score->sce_class, session.conn_class->cls_name) == 0) {
1977         ccur++;
1978       }
1979     }
1980   }
1981   pr_restore_scoreboard();
1982 
1983   key = "client-count";
1984   (void) pr_table_remove(session.notes, key, NULL);
1985   v = palloc(session.pool, sizeof(unsigned int));
1986   *((unsigned int *) v) = cur;
1987 
1988   if (pr_table_add(session.notes, key, v, sizeof(unsigned int)) < 0) {
1989     if (errno != EEXIST) {
1990       pr_log_pri(PR_LOG_WARNING,
1991         "warning: error stashing '%s': %s", key, strerror(errno));
1992     }
1993   }
1994 
1995   if (session.conn_class != NULL) {
1996     key = "class-client-count";
1997     (void) pr_table_remove(session.notes, key, NULL);
1998     v = palloc(session.pool, sizeof(unsigned int));
1999     *((unsigned int *) v) = ccur;
2000 
2001     if (pr_table_add(session.notes, key, v, sizeof(unsigned int)) < 0) {
2002       if (errno != EEXIST) {
2003         pr_log_pri(PR_LOG_WARNING,
2004           "warning: error stashing '%s': %s", key, strerror(errno));
2005       }
2006     }
2007   }
2008 
2009   /* Lookup any configured MaxConnectionsPerHost. */
2010   c = find_config(main_server->conf, CONF_PARAM, "MaxConnectionsPerHost",
2011     FALSE);
2012 
2013   if (c) {
2014     unsigned int *max = c->argv[0];
2015 
2016     if (*max &&
2017         hcur > *max) {
2018 
2019       char maxstr[20];
2020       char *msg = "Sorry, the maximum number of connections (%m) for your host "
2021         "are already connected.";
2022 
2023       pr_event_generate("mod_auth.max-connections-per-host", session.c);
2024 
2025       if (c->argc == 2)
2026         msg = c->argv[1];
2027 
2028       memset(maxstr, '\0', sizeof(maxstr));
2029       pr_snprintf(maxstr, sizeof(maxstr), "%u", *max);
2030       maxstr[sizeof(maxstr)-1] = '\0';
2031 
2032       pr_response_send(R_530, "%s", sreplace(session.pool, msg,
2033         "%m", maxstr, NULL));
2034 
2035       pr_log_auth(PR_LOG_NOTICE,
2036         "Connection refused (MaxConnectionsPerHost %u)", *max);
2037       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
2038         "Denied by MaxConnectionsPerHost");
2039     }
2040   }
2041 
2042   return 0;
2043 }
2044 
have_client_limits(cmd_rec * cmd)2045 static int have_client_limits(cmd_rec *cmd) {
2046   if (find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClientsPerClass", FALSE) != NULL) {
2047     return TRUE;
2048   }
2049 
2050   if (find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClientsPerHost", FALSE) != NULL) {
2051     return TRUE;
2052   }
2053 
2054   if (find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClientsPerUser", FALSE) != NULL) {
2055     return TRUE;
2056   }
2057 
2058   if (find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClients", FALSE) != NULL) {
2059     return TRUE;
2060   }
2061 
2062   if (find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxHostsPerUser", FALSE) != NULL) {
2063     return TRUE;
2064   }
2065 
2066   return FALSE;
2067 }
2068 
auth_count_scoreboard(cmd_rec * cmd,const char * user)2069 static int auth_count_scoreboard(cmd_rec *cmd, const char *user) {
2070   char *key;
2071   void *v;
2072   pr_scoreboard_entry_t *score = NULL;
2073   long cur = 0, hcur = 0, ccur = 0, hostsperuser = 1, usersessions = 0;
2074   config_rec *c = NULL, *maxc = NULL;
2075 
2076   /* First, check to see which Max* directives are configured.  If none
2077    * are configured, then there is no need for us to needlessly scan the
2078    * ScoreboardFile.
2079    */
2080   if (have_client_limits(cmd) == FALSE) {
2081     return 0;
2082   }
2083 
2084   /* Determine how many users are currently connected. */
2085 
2086   /* We use this call to get the possibly-changed user name. */
2087   c = pr_auth_get_anon_config(cmd->tmp_pool, &user, NULL, NULL);
2088 
2089   /* Gather our statistics. */
2090   if (user != NULL) {
2091     char curr_server_addr[80] = {'\0'};
2092 
2093     pr_snprintf(curr_server_addr, sizeof(curr_server_addr), "%s:%d",
2094       pr_netaddr_get_ipstr(session.c->local_addr), main_server->ServerPort);
2095     curr_server_addr[sizeof(curr_server_addr)-1] = '\0';
2096 
2097     if (pr_rewind_scoreboard() < 0) {
2098       pr_log_pri(PR_LOG_NOTICE, "error rewinding scoreboard: %s",
2099         strerror(errno));
2100     }
2101 
2102     while ((score = pr_scoreboard_entry_read()) != NULL) {
2103       unsigned char same_host = FALSE;
2104 
2105       pr_signals_handle();
2106 
2107       /* Make sure it matches our current server. */
2108       if (strcmp(score->sce_server_addr, curr_server_addr) == 0) {
2109 
2110         if ((c != NULL &&
2111              c->config_type == CONF_ANON &&
2112              strcmp(score->sce_user, user) == 0) ||
2113             c == NULL) {
2114 
2115           /* Only count authenticated clients, as per the documentation. */
2116           if (strncmp(score->sce_user, "(none)", 7) == 0) {
2117             continue;
2118           }
2119 
2120           cur++;
2121 
2122           /* Count up sessions on a per-host basis. */
2123 
2124           if (strcmp(score->sce_client_addr,
2125               pr_netaddr_get_ipstr(session.c->remote_addr)) == 0) {
2126             same_host = TRUE;
2127             hcur++;
2128           }
2129 
2130           /* Take a per-user count of connections. */
2131           if (strcmp(score->sce_user, user) == 0) {
2132             usersessions++;
2133 
2134             /* Count up unique hosts. */
2135             if (same_host == FALSE) {
2136               hostsperuser++;
2137             }
2138           }
2139         }
2140 
2141         if (session.conn_class != NULL &&
2142             strcasecmp(score->sce_class, session.conn_class->cls_name) == 0) {
2143           ccur++;
2144         }
2145       }
2146     }
2147     pr_restore_scoreboard();
2148     PRIVS_RELINQUISH
2149   }
2150 
2151   key = "client-count";
2152   (void) pr_table_remove(session.notes, key, NULL);
2153   v = palloc(session.pool, sizeof(unsigned int));
2154   *((unsigned int *) v) = cur;
2155 
2156   if (pr_table_add(session.notes, key, v, sizeof(unsigned int)) < 0) {
2157     if (errno != EEXIST) {
2158       pr_log_pri(PR_LOG_WARNING,
2159         "warning: error stashing '%s': %s", key, strerror(errno));
2160     }
2161   }
2162 
2163   if (session.conn_class != NULL) {
2164     key = "class-client-count";
2165     (void) pr_table_remove(session.notes, key, NULL);
2166     v = palloc(session.pool, sizeof(unsigned int));
2167     *((unsigned int *) v) = ccur;
2168 
2169     if (pr_table_add(session.notes, key, v, sizeof(unsigned int)) < 0) {
2170       if (errno != EEXIST) {
2171         pr_log_pri(PR_LOG_WARNING,
2172           "warning: error stashing '%s': %s", key, strerror(errno));
2173       }
2174     }
2175   }
2176 
2177   /* Try to determine what MaxClients/MaxHosts limits apply to this session
2178    * (if any) and count through the runtime file to see if this limit would
2179    * be exceeded.
2180    */
2181 
2182   maxc = find_config(cmd->server->conf, CONF_PARAM, "MaxClientsPerClass",
2183     FALSE);
2184   while (session.conn_class != NULL && maxc) {
2185     char *maxstr = "Sorry, the maximum number of clients (%m) from your class "
2186       "are already connected.";
2187     unsigned int *max = maxc->argv[1];
2188 
2189     if (strcmp(maxc->argv[0], session.conn_class->cls_name) != 0) {
2190       maxc = find_config_next(maxc, maxc->next, CONF_PARAM,
2191         "MaxClientsPerClass", FALSE);
2192       continue;
2193     }
2194 
2195     if (maxc->argc > 2) {
2196       maxstr = maxc->argv[2];
2197     }
2198 
2199     if (*max &&
2200         ccur > *max) {
2201       char maxn[20] = {'\0'};
2202 
2203       pr_event_generate("mod_auth.max-clients-per-class",
2204         session.conn_class->cls_name);
2205 
2206       pr_snprintf(maxn, sizeof(maxn), "%u", *max);
2207       pr_response_send(R_530, "%s", sreplace(cmd->tmp_pool, maxstr, "%m", maxn,
2208         NULL));
2209       (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
2210 
2211       pr_log_auth(PR_LOG_NOTICE,
2212         "Connection refused (MaxClientsPerClass %s %u)",
2213         session.conn_class->cls_name, *max);
2214       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
2215         "Denied by MaxClientsPerClass");
2216     }
2217 
2218     break;
2219   }
2220 
2221   maxc = find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClientsPerHost", FALSE);
2222   if (maxc) {
2223     char *maxstr = "Sorry, the maximum number of clients (%m) from your host "
2224       "are already connected.";
2225     unsigned int *max = maxc->argv[0];
2226 
2227     if (maxc->argc > 1) {
2228       maxstr = maxc->argv[1];
2229     }
2230 
2231     if (*max &&
2232         hcur > *max) {
2233       char maxn[20] = {'\0'};
2234 
2235       pr_event_generate("mod_auth.max-clients-per-host", session.c);
2236 
2237       pr_snprintf(maxn, sizeof(maxn), "%u", *max);
2238       pr_response_send(R_530, "%s", sreplace(cmd->tmp_pool, maxstr, "%m", maxn,
2239         NULL));
2240       (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
2241 
2242       pr_log_auth(PR_LOG_NOTICE,
2243         "Connection refused (MaxClientsPerHost %u)", *max);
2244       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
2245         "Denied by MaxClientsPerHost");
2246     }
2247   }
2248 
2249   /* Check for any configured MaxClientsPerUser. */
2250   maxc = find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClientsPerUser", FALSE);
2251   if (maxc) {
2252     char *maxstr = "Sorry, the maximum number of clients (%m) for this user "
2253       "are already connected.";
2254     unsigned int *max = maxc->argv[0];
2255 
2256     if (maxc->argc > 1) {
2257       maxstr = maxc->argv[1];
2258     }
2259 
2260     if (*max &&
2261         usersessions > *max) {
2262       char maxn[20] = {'\0'};
2263 
2264       pr_event_generate("mod_auth.max-clients-per-user", user);
2265 
2266       pr_snprintf(maxn, sizeof(maxn), "%u", *max);
2267       pr_response_send(R_530, "%s", sreplace(cmd->tmp_pool, maxstr, "%m", maxn,
2268         NULL));
2269       (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
2270 
2271       pr_log_auth(PR_LOG_NOTICE,
2272         "Connection refused (MaxClientsPerUser %u)", *max);
2273       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
2274         "Denied by MaxClientsPerUser");
2275     }
2276   }
2277 
2278   maxc = find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxClients", FALSE);
2279   if (maxc) {
2280     char *maxstr = "Sorry, the maximum number of allowed clients (%m) are "
2281       "already connected.";
2282     unsigned int *max = maxc->argv[0];
2283 
2284     if (maxc->argc > 1) {
2285       maxstr = maxc->argv[1];
2286     }
2287 
2288     if (*max &&
2289         cur > *max) {
2290       char maxn[20] = {'\0'};
2291 
2292       pr_event_generate("mod_auth.max-clients", NULL);
2293 
2294       pr_snprintf(maxn, sizeof(maxn), "%u", *max);
2295       pr_response_send(R_530, "%s", sreplace(cmd->tmp_pool, maxstr, "%m", maxn,
2296         NULL));
2297       (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
2298 
2299       pr_log_auth(PR_LOG_NOTICE, "Connection refused (MaxClients %u)", *max);
2300       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
2301         "Denied by MaxClients");
2302     }
2303   }
2304 
2305   maxc = find_config(TOPLEVEL_CONF, CONF_PARAM, "MaxHostsPerUser", FALSE);
2306   if (maxc) {
2307     char *maxstr = "Sorry, the maximum number of hosts (%m) for this user are "
2308       "already connected.";
2309     unsigned int *max = maxc->argv[0];
2310 
2311     if (maxc->argc > 1) {
2312       maxstr = maxc->argv[1];
2313     }
2314 
2315     if (*max && hostsperuser > *max) {
2316       char maxn[20] = {'\0'};
2317 
2318       pr_event_generate("mod_auth.max-hosts-per-user", user);
2319 
2320       pr_snprintf(maxn, sizeof(maxn), "%u", *max);
2321       pr_response_send(R_530, "%s", sreplace(cmd->tmp_pool, maxstr, "%m", maxn,
2322         NULL));
2323       (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
2324 
2325       pr_log_auth(PR_LOG_NOTICE, "Connection refused (MaxHostsPerUser %u)",
2326         *max);
2327       pr_session_disconnect(&auth_module, PR_SESS_DISCONNECT_CONFIG_ACL,
2328         "Denied by MaxHostsPerUser");
2329     }
2330   }
2331 
2332   return 0;
2333 }
2334 
auth_pre_user(cmd_rec * cmd)2335 MODRET auth_pre_user(cmd_rec *cmd) {
2336 
2337   if (saw_first_user_cmd == FALSE) {
2338     if (pr_trace_get_level(timing_channel)) {
2339       unsigned long elapsed_ms;
2340       uint64_t finish_ms;
2341 
2342       pr_gettimeofday_millis(&finish_ms);
2343       elapsed_ms = (unsigned long) (finish_ms - session.connect_time_ms);
2344 
2345       pr_trace_msg(timing_channel, 4, "Time before first USER: %lu ms",
2346         elapsed_ms);
2347     }
2348     saw_first_user_cmd = TRUE;
2349   }
2350 
2351   if (logged_in) {
2352     return PR_DECLINED(cmd);
2353   }
2354 
2355   /* Close the passwd and group databases, because libc won't let us see new
2356    * entries to these files without this (only in PersistentPasswd mode).
2357    */
2358   pr_auth_endpwent(cmd->tmp_pool);
2359   pr_auth_endgrent(cmd->tmp_pool);
2360 
2361   /* Check for a user name that exceeds PR_TUNABLE_LOGIN_MAX. */
2362   if (strlen(cmd->arg) > PR_TUNABLE_LOGIN_MAX) {
2363     pr_log_pri(PR_LOG_NOTICE, "USER %s (Login failed): "
2364       "maximum USER length exceeded", cmd->arg);
2365     pr_response_add_err(R_501, _("Login incorrect."));
2366 
2367     pr_cmd_set_errno(cmd, EPERM);
2368     errno = EPERM;
2369     return PR_ERROR(cmd);
2370   }
2371 
2372   return PR_DECLINED(cmd);
2373 }
2374 
auth_user(cmd_rec * cmd)2375 MODRET auth_user(cmd_rec *cmd) {
2376   int nopass = FALSE;
2377   config_rec *c;
2378   const char *denymsg = NULL, *user, *origuser;
2379   unsigned char *anon_require_passwd = NULL;
2380 
2381   if (cmd->argc < 2) {
2382     return PR_ERROR_MSG(cmd, R_500, _("USER: command requires a parameter"));
2383   }
2384 
2385   if (logged_in) {
2386     /* If the client has already authenticated, BUT the given USER command
2387      * here is for the exact same user name, then allow the command to
2388      * succeed (Bug#4217).
2389      */
2390     origuser = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
2391     if (origuser != NULL &&
2392         strcmp(origuser, cmd->arg) == 0) {
2393       pr_response_add(R_230, _("User %s logged in"), origuser);
2394       return PR_HANDLED(cmd);
2395     }
2396 
2397     pr_response_add_err(R_501, "%s", _("Reauthentication not supported"));
2398     return PR_ERROR(cmd);
2399   }
2400 
2401   user = cmd->arg;
2402 
2403   (void) pr_table_remove(session.notes, "mod_auth.orig-user", NULL);
2404   (void) pr_table_remove(session.notes, "mod_auth.anon-passwd", NULL);
2405 
2406   if (pr_table_add_dup(session.notes, "mod_auth.orig-user", user, 0) < 0) {
2407     pr_log_debug(DEBUG3, "error stashing 'mod_auth.orig-user' in "
2408       "session.notes: %s", strerror(errno));
2409   }
2410 
2411   origuser = user;
2412   c = pr_auth_get_anon_config(cmd->tmp_pool, &user, NULL, NULL);
2413 
2414   /* Check for AccessDenyMsg */
2415   denymsg = get_param_ptr((c ? c->subset : cmd->server->conf), "AccessDenyMsg",
2416     FALSE);
2417   if (denymsg != NULL) {
2418     if (strstr(denymsg, "%u") != NULL) {
2419       denymsg = sreplace(cmd->tmp_pool, denymsg, "%u", user, NULL);
2420     }
2421   }
2422 
2423   if (c != NULL) {
2424     anon_require_passwd = get_param_ptr(c->subset, "AnonRequirePassword",
2425       FALSE);
2426   }
2427 
2428   if (c && user && (!anon_require_passwd || *anon_require_passwd == FALSE))
2429     nopass = TRUE;
2430 
2431   session.gids = NULL;
2432   session.groups = NULL;
2433   session.user = NULL;
2434   session.group = NULL;
2435 
2436   if (nopass) {
2437     pr_response_add(R_331, _("Anonymous login ok, send your complete email "
2438       "address as your password"));
2439 
2440   } else if (pr_auth_requires_pass(cmd->tmp_pool, user) == FALSE) {
2441     /* Check to see if a password from the client is required.  In the
2442      * vast majority of cases, a password will be required.
2443      */
2444 
2445     /* Act as if we received a PASS command from the client. */
2446     cmd_rec *fakecmd = pr_cmd_alloc(cmd->pool, 2, NULL);
2447 
2448     /* We use pstrdup() here, rather than assigning C_PASS directly, since
2449      * code elsewhere will attempt to modify this buffer, and C_PASS is
2450      * a string literal.
2451      */
2452     fakecmd->argv[0] = pstrdup(fakecmd->pool, C_PASS);
2453     fakecmd->argv[1] = NULL;
2454     fakecmd->arg = NULL;
2455 
2456     c = add_config_param_set(&cmd->server->conf, "authenticated", 1, NULL);
2457     c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
2458     *((unsigned char *) c->argv[0]) = TRUE;
2459 
2460     authenticated_without_pass = TRUE;
2461     pr_log_auth(PR_LOG_NOTICE, "USER %s: Authenticated without password", user);
2462 
2463     pr_cmd_dispatch(fakecmd);
2464 
2465   } else {
2466     pr_response_add(R_331, _("Password required for %s"),
2467       (char *) cmd->argv[1]);
2468   }
2469 
2470   return PR_HANDLED(cmd);
2471 }
2472 
2473 /* Close the passwd and group databases, similar to auth_pre_user(). */
auth_pre_pass(cmd_rec * cmd)2474 MODRET auth_pre_pass(cmd_rec *cmd) {
2475   const char *user;
2476   char *displaylogin;
2477 
2478   pr_auth_endpwent(cmd->tmp_pool);
2479   pr_auth_endgrent(cmd->tmp_pool);
2480 
2481   /* Handle cases where PASS might be sent before USER. */
2482   user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
2483   if (user != NULL) {
2484     config_rec *c;
2485 
2486     c = find_config(main_server->conf, CONF_PARAM, "AllowEmptyPasswords",
2487       FALSE);
2488     if (c == NULL) {
2489       const char *anon_user;
2490       config_rec *anon_config;
2491 
2492       /* Since we have not authenticated yet, we cannot use the TOPLEVEL_CONF
2493        * macro to handle <Anonymous> sections.  So we do it manually.
2494        */
2495       anon_user = pstrdup(cmd->tmp_pool, user);
2496       anon_config = pr_auth_get_anon_config(cmd->tmp_pool, &anon_user, NULL,
2497         NULL);
2498       if (anon_config != NULL) {
2499         c = find_config(anon_config->subset, CONF_PARAM, "AllowEmptyPasswords",
2500           FALSE);
2501       }
2502     }
2503 
2504     if (c != NULL) {
2505       int allow_empty_passwords;
2506 
2507       allow_empty_passwords = *((int *) c->argv[0]);
2508       if (allow_empty_passwords == FALSE) {
2509         const char *proto;
2510         int reject_empty_passwd = FALSE, using_ssh2 = FALSE;
2511         size_t passwd_len = 0;
2512 
2513         proto = pr_session_get_protocol(0);
2514         if (strcmp(proto, "ssh2") == 0) {
2515           using_ssh2 = TRUE;
2516         }
2517 
2518         if (cmd->argc > 1) {
2519           if (cmd->arg != NULL) {
2520             passwd_len = strlen(cmd->arg);
2521           }
2522         }
2523 
2524         if (passwd_len == 0) {
2525           reject_empty_passwd = TRUE;
2526 
2527           /* Make sure to NOT enforce 'AllowEmptyPasswords off' if e.g.
2528            * the AllowDotLogin TLSOption is in effect, or if the protocol is
2529            * SSH2 (for mod_sftp uses "fake" PASS commands for the SSH login
2530            * protocol).
2531            */
2532 
2533           if (session.auth_mech != NULL &&
2534               strcmp(session.auth_mech, "mod_tls.c") == 0) {
2535             pr_log_debug(DEBUG9, "%s", "'AllowEmptyPasswords off' in effect, "
2536               "BUT client authenticated via the AllowDotLogin TLSOption");
2537             reject_empty_passwd = FALSE;
2538           }
2539 
2540           if (using_ssh2 == TRUE) {
2541             reject_empty_passwd = FALSE;
2542           }
2543         }
2544 
2545         if (reject_empty_passwd == TRUE) {
2546           pr_log_debug(DEBUG5,
2547             "Refusing empty password from user '%s' (AllowEmptyPasswords "
2548             "false)", user);
2549           pr_log_auth(PR_LOG_NOTICE,
2550             "Refusing empty password from user '%s'", user);
2551 
2552           pr_event_generate("mod_auth.empty-password", user);
2553           pr_response_add_err(R_501, _("Login incorrect."));
2554           return PR_ERROR(cmd);
2555         }
2556       }
2557     }
2558   }
2559 
2560   /* Look for a DisplayLogin file which has an absolute path.  If we find one,
2561    * open a filehandle, such that that file can be displayed even if the
2562    * session is chrooted.  DisplayLogin files with relative paths will be
2563    * handled after chroot, preserving the old behavior.
2564    */
2565 
2566   displaylogin = get_param_ptr(TOPLEVEL_CONF, "DisplayLogin", FALSE);
2567   if (displaylogin &&
2568       *displaylogin == '/') {
2569     struct stat st;
2570 
2571     displaylogin_fh = pr_fsio_open(displaylogin, O_RDONLY);
2572     if (displaylogin_fh == NULL) {
2573       pr_log_debug(DEBUG6, "unable to open DisplayLogin file '%s': %s",
2574         displaylogin, strerror(errno));
2575 
2576     } else {
2577       if (pr_fsio_fstat(displaylogin_fh, &st) < 0) {
2578         pr_log_debug(DEBUG6, "unable to stat DisplayLogin file '%s': %s",
2579           displaylogin, strerror(errno));
2580         pr_fsio_close(displaylogin_fh);
2581         displaylogin_fh = NULL;
2582 
2583       } else {
2584         if (S_ISDIR(st.st_mode)) {
2585           errno = EISDIR;
2586           pr_log_debug(DEBUG6, "unable to use DisplayLogin file '%s': %s",
2587             displaylogin, strerror(errno));
2588           pr_fsio_close(displaylogin_fh);
2589           displaylogin_fh = NULL;
2590         }
2591       }
2592     }
2593   }
2594 
2595   return PR_DECLINED(cmd);
2596 }
2597 
auth_pass(cmd_rec * cmd)2598 MODRET auth_pass(cmd_rec *cmd) {
2599   const char *user = NULL;
2600   int res = 0;
2601 
2602   if (logged_in) {
2603     return PR_ERROR_MSG(cmd, R_503, _("You are already logged in"));
2604   }
2605 
2606   user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
2607   if (user == NULL) {
2608     (void) pr_table_remove(session.notes, "mod_auth.orig-user", NULL);
2609     (void) pr_table_remove(session.notes, "mod_auth.anon-passwd", NULL);
2610 
2611     return PR_ERROR_MSG(cmd, R_503, _("Login with USER first"));
2612   }
2613 
2614   /* Clear any potentially cached directory config */
2615   session.anon_config = NULL;
2616   session.dir_config = NULL;
2617 
2618   res = setup_env(cmd->tmp_pool, cmd, user, cmd->arg);
2619   if (res == 1) {
2620     config_rec *c = NULL;
2621 
2622     c = add_config_param_set(&cmd->server->conf, "authenticated", 1, NULL);
2623     c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
2624     *((unsigned char *) c->argv[0]) = TRUE;
2625 
2626     set_auth_check(NULL);
2627 
2628     (void) pr_table_remove(session.notes, "mod_auth.anon-passwd", NULL);
2629 
2630     if (session.sf_flags & SF_ANON) {
2631       if (pr_table_add_dup(session.notes, "mod_auth.anon-passwd",
2632           pr_fs_decode_path(cmd->server->pool, cmd->arg), 0) < 0) {
2633         pr_log_debug(DEBUG3,
2634           "error stashing anonymous password in session.notes: %s",
2635           strerror(errno));
2636       }
2637     }
2638 
2639     logged_in = TRUE;
2640 
2641     if (pr_trace_get_level(timing_channel)) {
2642       unsigned long elapsed_ms;
2643       uint64_t finish_ms;
2644 
2645       pr_gettimeofday_millis(&finish_ms);
2646       elapsed_ms = (unsigned long) (finish_ms - session.connect_time_ms);
2647 
2648       pr_trace_msg(timing_channel, 4,
2649         "Time before successful login (via '%s'): %lu ms", session.auth_mech,
2650         elapsed_ms);
2651     }
2652 
2653     return PR_HANDLED(cmd);
2654   }
2655 
2656   (void) pr_table_remove(session.notes, "mod_auth.anon-passwd", NULL);
2657 
2658   if (res == 0) {
2659     unsigned int max_logins, *max = NULL;
2660     const char *denymsg = NULL;
2661 
2662     /* check for AccessDenyMsg */
2663     if ((denymsg = get_param_ptr((session.anon_config ?
2664         session.anon_config->subset : cmd->server->conf),
2665         "AccessDenyMsg", FALSE)) != NULL) {
2666 
2667       if (strstr(denymsg, "%u") != NULL) {
2668         denymsg = sreplace(cmd->tmp_pool, denymsg, "%u", user, NULL);
2669       }
2670     }
2671 
2672     max = get_param_ptr(main_server->conf, "MaxLoginAttempts", FALSE);
2673     if (max != NULL) {
2674       max_logins = *max;
2675 
2676     } else {
2677       max_logins = 3;
2678     }
2679 
2680     if (max_logins > 0 &&
2681         ((unsigned int) ++auth_tries) >= max_logins) {
2682       if (denymsg) {
2683         pr_response_send(R_530, "%s", denymsg);
2684 
2685       } else {
2686         pr_response_send(R_530, "%s", _("Login incorrect."));
2687       }
2688 
2689       pr_log_auth(PR_LOG_NOTICE,
2690         "Maximum login attempts (%u) exceeded, connection refused", max_logins);
2691 
2692       /* Generate an event about this limit being exceeded. */
2693       pr_event_generate("mod_auth.max-login-attempts", session.c);
2694 
2695       /* Set auth_tries to -1 so that the session is disconnected after
2696        * POST_CMD_ERR and LOG_CMD_ERR events are processed.
2697        */
2698       auth_tries = -1;
2699     }
2700 
2701     return PR_ERROR_MSG(cmd, R_530, denymsg ? denymsg : _("Login incorrect."));
2702   }
2703 
2704   return PR_HANDLED(cmd);
2705 }
2706 
auth_acct(cmd_rec * cmd)2707 MODRET auth_acct(cmd_rec *cmd) {
2708   pr_response_add(R_502, _("ACCT command not implemented"));
2709   return PR_HANDLED(cmd);
2710 }
2711 
auth_rein(cmd_rec * cmd)2712 MODRET auth_rein(cmd_rec *cmd) {
2713   pr_response_add(R_502, _("REIN command not implemented"));
2714   return PR_HANDLED(cmd);
2715 }
2716 
2717 /* FSIO callbacks for providing a fake robots.txt file, for the AnonAllowRobots
2718  * functionality.
2719  */
2720 
2721 #define AUTH_ROBOTS_TXT			"User-agent: *\nDisallow: /\n"
2722 #define AUTH_ROBOTS_TXT_FD		6742
2723 
robots_fsio_stat(pr_fs_t * fs,const char * path,struct stat * st)2724 static int robots_fsio_stat(pr_fs_t *fs, const char *path, struct stat *st) {
2725   st->st_dev = (dev_t) 0;
2726   st->st_ino = (ino_t) 0;
2727   st->st_mode = (S_IFREG|S_IRUSR|S_IRGRP|S_IROTH);
2728   st->st_nlink = 0;
2729   st->st_uid = (uid_t) 0;
2730   st->st_gid = (gid_t) 0;
2731   st->st_atime = 0;
2732   st->st_mtime = 0;
2733   st->st_ctime = 0;
2734   st->st_size = strlen(AUTH_ROBOTS_TXT);
2735   st->st_blksize = 1024;
2736   st->st_blocks = 1;
2737 
2738   return 0;
2739 }
2740 
robots_fsio_fstat(pr_fh_t * fh,int fd,struct stat * st)2741 static int robots_fsio_fstat(pr_fh_t *fh, int fd, struct stat *st) {
2742   if (fd != AUTH_ROBOTS_TXT_FD) {
2743     errno = EINVAL;
2744     return -1;
2745   }
2746 
2747   return robots_fsio_stat(NULL, NULL, st);
2748 }
2749 
robots_fsio_lstat(pr_fs_t * fs,const char * path,struct stat * st)2750 static int robots_fsio_lstat(pr_fs_t *fs, const char *path, struct stat *st) {
2751   return robots_fsio_stat(fs, path, st);
2752 }
2753 
robots_fsio_unlink(pr_fs_t * fs,const char * path)2754 static int robots_fsio_unlink(pr_fs_t *fs, const char *path) {
2755   return 0;
2756 }
2757 
robots_fsio_open(pr_fh_t * fh,const char * path,int flags)2758 static int robots_fsio_open(pr_fh_t *fh, const char *path, int flags) {
2759   if (flags != O_RDONLY) {
2760     errno = EINVAL;
2761     return -1;
2762   }
2763 
2764   return AUTH_ROBOTS_TXT_FD;
2765 }
2766 
robots_fsio_close(pr_fh_t * fh,int fd)2767 static int robots_fsio_close(pr_fh_t *fh, int fd) {
2768   if (fd != AUTH_ROBOTS_TXT_FD) {
2769     errno = EINVAL;
2770     return -1;
2771   }
2772 
2773   return 0;
2774 }
2775 
robots_fsio_read(pr_fh_t * fh,int fd,char * buf,size_t bufsz)2776 static int robots_fsio_read(pr_fh_t *fh, int fd, char *buf, size_t bufsz) {
2777   size_t robots_len;
2778 
2779   if (fd != AUTH_ROBOTS_TXT_FD) {
2780     errno = EINVAL;
2781     return -1;
2782   }
2783 
2784   robots_len = strlen(AUTH_ROBOTS_TXT);
2785 
2786   if (bufsz < robots_len) {
2787     errno = EINVAL;
2788     return -1;
2789   }
2790 
2791   memcpy(buf, AUTH_ROBOTS_TXT, robots_len);
2792   return (int) robots_len;
2793 }
2794 
robots_fsio_write(pr_fh_t * fh,int fd,const char * buf,size_t bufsz)2795 static int robots_fsio_write(pr_fh_t *fh, int fd, const char *buf,
2796     size_t bufsz) {
2797   if (fd != AUTH_ROBOTS_TXT_FD) {
2798     errno = EINVAL;
2799     return -1;
2800   }
2801 
2802   return (int) bufsz;
2803 }
2804 
robots_fsio_access(pr_fs_t * fs,const char * path,int mode,uid_t uid,gid_t gid,array_header * suppl_gids)2805 static int robots_fsio_access(pr_fs_t *fs, const char *path, int mode,
2806     uid_t uid, gid_t gid, array_header *suppl_gids) {
2807   if (mode != R_OK) {
2808     errno = EACCES;
2809     return -1;
2810   }
2811 
2812   return 0;
2813 }
2814 
robots_fsio_faccess(pr_fh_t * fh,int mode,uid_t uid,gid_t gid,array_header * suppl_gids)2815 static int robots_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
2816     array_header *suppl_gids) {
2817 
2818   if (fh->fh_fd != AUTH_ROBOTS_TXT_FD) {
2819     errno = EINVAL;
2820     return -1;
2821   }
2822 
2823   if (mode != R_OK) {
2824     errno = EACCES;
2825     return -1;
2826   }
2827 
2828   return 0;
2829 }
2830 
auth_pre_retr(cmd_rec * cmd)2831 MODRET auth_pre_retr(cmd_rec *cmd) {
2832   const char *path;
2833   pr_fs_t *curr_fs = NULL;
2834   struct stat st;
2835 
2836   /* Only apply this for <Anonymous> logins. */
2837   if (session.anon_config == NULL) {
2838     return PR_DECLINED(cmd);
2839   }
2840 
2841   if (auth_anon_allow_robots == TRUE) {
2842     return PR_DECLINED(cmd);
2843   }
2844 
2845   auth_anon_allow_robots_enabled = FALSE;
2846 
2847   path = dir_canonical_path(cmd->tmp_pool, cmd->arg);
2848   if (strcasecmp(path, "/robots.txt") != 0) {
2849     return PR_DECLINED(cmd);
2850   }
2851 
2852   /* If a previous REST command, with a non-zero value, has been sent, then
2853    * do nothing.  Ugh.
2854    */
2855   if (session.restart_pos > 0) {
2856     pr_log_debug(DEBUG10, "'AnonAllowRobots off' in effect, but cannot "
2857       "support resumed download (REST %" PR_LU " previously sent by client)",
2858       (pr_off_t) session.restart_pos);
2859     return PR_DECLINED(cmd);
2860   }
2861 
2862   pr_fs_clear_cache2(path);
2863   if (pr_fsio_lstat(path, &st) == 0) {
2864     /* There's an existing REAL "robots.txt" file on disk; use that, and
2865      * preserve the principle of least surprise.
2866      */
2867     pr_log_debug(DEBUG10, "'AnonAllowRobots off' in effect, but have "
2868       "real 'robots.txt' file on disk; using that");
2869     return PR_DECLINED(cmd);
2870   }
2871 
2872   curr_fs = pr_get_fs(path, NULL);
2873   if (curr_fs != NULL) {
2874     pr_fs_t *robots_fs;
2875 
2876     robots_fs = pr_register_fs(cmd->pool, "robots", path);
2877     if (robots_fs == NULL) {
2878       pr_log_debug(DEBUG8, "'AnonAllowRobots off' in effect, but failed to "
2879         "register FS: %s", strerror(errno));
2880       return PR_DECLINED(cmd);
2881     }
2882 
2883     /* Use enough of our own custom FSIO callbacks to be able to provide
2884      * a fake "robots.txt" file.
2885      */
2886     robots_fs->stat = robots_fsio_stat;
2887     robots_fs->fstat = robots_fsio_fstat;
2888     robots_fs->lstat = robots_fsio_lstat;
2889     robots_fs->unlink = robots_fsio_unlink;
2890     robots_fs->open = robots_fsio_open;
2891     robots_fs->close = robots_fsio_close;
2892     robots_fs->read = robots_fsio_read;
2893     robots_fs->write = robots_fsio_write;
2894     robots_fs->access = robots_fsio_access;
2895     robots_fs->faccess = robots_fsio_faccess;
2896 
2897     /* For all other FSIO callbacks, use the underlying FS. */
2898     robots_fs->rename = curr_fs->rename;
2899     robots_fs->lseek = curr_fs->lseek;
2900     robots_fs->link = curr_fs->link;
2901     robots_fs->readlink = curr_fs->readlink;
2902     robots_fs->symlink = curr_fs->symlink;
2903     robots_fs->ftruncate = curr_fs->ftruncate;
2904     robots_fs->truncate = curr_fs->truncate;
2905     robots_fs->chmod = curr_fs->chmod;
2906     robots_fs->fchmod = curr_fs->fchmod;
2907     robots_fs->chown = curr_fs->chown;
2908     robots_fs->fchown = curr_fs->fchown;
2909     robots_fs->lchown = curr_fs->lchown;
2910     robots_fs->utimes = curr_fs->utimes;
2911     robots_fs->futimes = curr_fs->futimes;
2912     robots_fs->fsync = curr_fs->fsync;
2913 
2914     pr_fs_clear_cache2(path);
2915     auth_anon_allow_robots_enabled = TRUE;
2916   }
2917 
2918   return PR_DECLINED(cmd);
2919 }
2920 
auth_post_retr(cmd_rec * cmd)2921 MODRET auth_post_retr(cmd_rec *cmd) {
2922   if (auth_anon_allow_robots == TRUE) {
2923     return PR_DECLINED(cmd);
2924   }
2925 
2926   if (auth_anon_allow_robots_enabled == TRUE) {
2927     int res;
2928 
2929     res = pr_unregister_fs("/robots.txt");
2930     if (res < 0) {
2931       pr_log_debug(DEBUG9, "error removing 'robots' FS for '/robots.txt': %s",
2932         strerror(errno));
2933     }
2934 
2935     auth_anon_allow_robots_enabled = FALSE;
2936   }
2937 
2938   return PR_DECLINED(cmd);
2939 }
2940 
2941 /* Configuration handlers
2942  */
2943 
set_accessdenymsg(cmd_rec * cmd)2944 MODRET set_accessdenymsg(cmd_rec *cmd) {
2945   config_rec *c = NULL;
2946 
2947   CHECK_ARGS(cmd, 1);
2948   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2949 
2950   c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2951   c->flags |= CF_MERGEDOWN;
2952 
2953   return PR_HANDLED(cmd);
2954 }
2955 
set_accessgrantmsg(cmd_rec * cmd)2956 MODRET set_accessgrantmsg(cmd_rec *cmd) {
2957   config_rec *c = NULL;
2958 
2959   CHECK_ARGS(cmd, 1);
2960   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2961 
2962   c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2963   c->flags |= CF_MERGEDOWN;
2964 
2965   return PR_HANDLED(cmd);
2966 }
2967 
2968 /* usage: AllowChrootSymlinks on|off */
set_allowchrootsymlinks(cmd_rec * cmd)2969 MODRET set_allowchrootsymlinks(cmd_rec *cmd) {
2970   int allow_chroot_symlinks = -1;
2971   config_rec *c = NULL;
2972 
2973   CHECK_ARGS(cmd, 1);
2974   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2975 
2976   allow_chroot_symlinks = get_boolean(cmd, 1);
2977   if (allow_chroot_symlinks == -1) {
2978     CONF_ERROR(cmd, "expected Boolean parameter");
2979   }
2980 
2981   c = add_config_param(cmd->argv[0], 1, NULL);
2982   c->argv[0] = pcalloc(c->pool, sizeof(int));
2983   *((int *) c->argv[0]) = allow_chroot_symlinks;
2984 
2985   return PR_HANDLED(cmd);
2986 }
2987 
2988 /* usage: AllowEmptyPasswords on|off */
set_allowemptypasswords(cmd_rec * cmd)2989 MODRET set_allowemptypasswords(cmd_rec *cmd) {
2990   int allow_empty_passwords = -1;
2991   config_rec *c = NULL;
2992 
2993   CHECK_ARGS(cmd, 1);
2994   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2995 
2996   allow_empty_passwords = get_boolean(cmd, 1);
2997   if (allow_empty_passwords == -1) {
2998     CONF_ERROR(cmd, "expected Boolean parameter");
2999   }
3000 
3001   c = add_config_param(cmd->argv[0], 1, NULL);
3002   c->argv[0] = pcalloc(c->pool, sizeof(int));
3003   *((int *) c->argv[0]) = allow_empty_passwords;
3004   c->flags |= CF_MERGEDOWN;
3005 
3006   return PR_HANDLED(cmd);
3007 }
3008 
3009 /* usage: AnonAllowRobots on|off */
set_anonallowrobots(cmd_rec * cmd)3010 MODRET set_anonallowrobots(cmd_rec *cmd) {
3011   int allow_robots = -1;
3012   config_rec *c;
3013 
3014   CHECK_ARGS(cmd, 1);
3015   CHECK_CONF(cmd, CONF_ANON);
3016 
3017   allow_robots = get_boolean(cmd, 1);
3018   if (allow_robots == -1) {
3019     CONF_ERROR(cmd, "expected Boolean parameter");
3020   }
3021 
3022   c = add_config_param(cmd->argv[0], 1, NULL);
3023   c->argv[0] = palloc(c->pool, sizeof(int));
3024   *((int *) c->argv[0]) = allow_robots;
3025 
3026   return PR_HANDLED(cmd);
3027 }
3028 
set_anonrequirepassword(cmd_rec * cmd)3029 MODRET set_anonrequirepassword(cmd_rec *cmd) {
3030   int bool = -1;
3031   config_rec *c = NULL;
3032 
3033   CHECK_ARGS(cmd, 1);
3034   CHECK_CONF(cmd, CONF_ANON);
3035 
3036   bool = get_boolean(cmd, 1);
3037   if (bool == -1)
3038     CONF_ERROR(cmd, "expected Boolean parameter");
3039 
3040   c = add_config_param(cmd->argv[0], 1, NULL);
3041   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3042   *((unsigned char *) c->argv[0]) = bool;
3043 
3044   return PR_HANDLED(cmd);
3045 }
3046 
3047 /* usage: AnonRejectPasswords pattern [flags] */
set_anonrejectpasswords(cmd_rec * cmd)3048 MODRET set_anonrejectpasswords(cmd_rec *cmd) {
3049 #ifdef PR_USE_REGEX
3050   config_rec *c;
3051   pr_regex_t *pre = NULL;
3052   int notmatch = FALSE, regex_flags = REG_EXTENDED|REG_NOSUB, res = 0;
3053   char *pattern = NULL;
3054 
3055   if (cmd->argc-1 < 1 ||
3056       cmd->argc-1 > 2) {
3057     CONF_ERROR(cmd, "bad number of parameters");
3058   }
3059 
3060   CHECK_CONF(cmd, CONF_ANON);
3061 
3062   /* Make sure that, if present, the flags parameter is correctly formatted. */
3063   if (cmd->argc-1 == 2) {
3064     int flags = 0;
3065 
3066     /* We need to parse the flags parameter here, to see if any flags which
3067      * affect the compilation of the regex (e.g. NC) are present.
3068      */
3069 
3070     flags = pr_filter_parse_flags(cmd->tmp_pool, cmd->argv[2]);
3071     if (flags < 0) {
3072       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
3073         ": badly formatted flags parameter: '", cmd->argv[2], "'", NULL));
3074     }
3075 
3076     if (flags == 0) {
3077       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
3078         ": unknown flags '", cmd->argv[2], "'", NULL));
3079     }
3080 
3081     regex_flags |= flags;
3082   }
3083 
3084   pre = pr_regexp_alloc(&auth_module);
3085 
3086   pattern = cmd->argv[1];
3087   if (*pattern == '!') {
3088     notmatch = TRUE;
3089     pattern++;
3090   }
3091 
3092   res = pr_regexp_compile(pre, pattern, regex_flags);
3093   if (res != 0) {
3094     char errstr[200] = {'\0'};
3095 
3096     pr_regexp_error(res, pre, errstr, 200);
3097     pr_regexp_free(NULL, pre);
3098 
3099     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "Unable to compile regex '",
3100       cmd->argv[1], "': ", errstr, NULL));
3101   }
3102 
3103   c = add_config_param(cmd->argv[0], 2, pre, NULL);
3104   c->argv[1] = palloc(c->pool, sizeof(int));
3105   *((int *) c->argv[1]) = notmatch;
3106   return PR_HANDLED(cmd);
3107 
3108 #else
3109   CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "The ", cmd->argv[0], " directive "
3110     "cannot be used on this system, as you do not have POSIX compliant "
3111     "regex support", NULL));
3112 #endif
3113 }
3114 
set_authaliasonly(cmd_rec * cmd)3115 MODRET set_authaliasonly(cmd_rec *cmd) {
3116   int bool = -1;
3117   config_rec *c = NULL;
3118 
3119   CHECK_ARGS(cmd, 1);
3120   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3121 
3122   bool = get_boolean(cmd, 1);
3123   if (bool == -1)
3124     CONF_ERROR(cmd, "expected Boolean parameter");
3125 
3126   c = add_config_param(cmd->argv[0], 1, NULL);
3127   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3128   *((unsigned char *) c->argv[0]) = bool;
3129 
3130   c->flags |= CF_MERGEDOWN;
3131   return PR_HANDLED(cmd);
3132 }
3133 
set_authusingalias(cmd_rec * cmd)3134 MODRET set_authusingalias(cmd_rec *cmd) {
3135   int bool = -1;
3136   config_rec *c = NULL;
3137 
3138   CHECK_ARGS(cmd, 1);
3139   CHECK_CONF(cmd, CONF_ANON);
3140 
3141   bool = get_boolean(cmd, 1);
3142   if (bool == -1)
3143     CONF_ERROR(cmd, "expected Boolean parameter");
3144 
3145   c = add_config_param(cmd->argv[0], 1, NULL);
3146   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3147   *((unsigned char *) c->argv[0]) = bool;
3148 
3149   return PR_HANDLED(cmd);
3150 }
3151 
set_createhome(cmd_rec * cmd)3152 MODRET set_createhome(cmd_rec *cmd) {
3153   int bool = -1, start = 2;
3154   mode_t mode = (mode_t) 0700, dirmode = (mode_t) 0711;
3155   char *skel_path = NULL;
3156   config_rec *c = NULL;
3157   uid_t cuid = 0;
3158   gid_t cgid = 0, hgid = -1;
3159   unsigned long flags = 0UL;
3160 
3161   if (cmd->argc-1 < 1) {
3162     CONF_ERROR(cmd, "wrong number of parameters");
3163   }
3164 
3165   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3166 
3167   bool = get_boolean(cmd, 1);
3168   if (bool == -1) {
3169     CONF_ERROR(cmd, "expected Boolean parameter");
3170   }
3171 
3172   /* No need to process the rest if bool is FALSE. */
3173   if (bool == FALSE) {
3174     c = add_config_param(cmd->argv[0], 1, NULL);
3175     c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3176     *((unsigned char *) c->argv[0]) = bool;
3177 
3178     return PR_HANDLED(cmd);
3179   }
3180 
3181   /* Check the mode parameter, if present */
3182   if (cmd->argc-1 >= 2 &&
3183       strcasecmp(cmd->argv[2], "dirmode") != 0 &&
3184       strcasecmp(cmd->argv[2], "skel") != 0) {
3185     char *tmp = NULL;
3186 
3187     mode = strtol(cmd->argv[2], &tmp, 8);
3188 
3189     if (tmp && *tmp)
3190       CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": bad mode parameter: '",
3191         cmd->argv[2], "'", NULL));
3192 
3193     start = 3;
3194   }
3195 
3196   if (cmd->argc-1 > 2) {
3197     register unsigned int i;
3198 
3199     /* Cycle through the rest of the parameters */
3200     for (i = start; i < cmd->argc;) {
3201       if (strcasecmp(cmd->argv[i], "skel") == 0) {
3202         struct stat st;
3203 
3204         /* Check that the skel directory, if configured, meets the
3205          * requirements.
3206          */
3207 
3208         skel_path = cmd->argv[++i];
3209 
3210         if (*skel_path != '/') {
3211           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "skel path '",
3212             skel_path, "' is not a full path", NULL));
3213         }
3214 
3215         if (pr_fsio_stat(skel_path, &st) < 0) {
3216           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to stat '",
3217             skel_path, "': ", strerror(errno), NULL));
3218         }
3219 
3220         if (!S_ISDIR(st.st_mode)) {
3221           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", skel_path,
3222             "' is not a directory", NULL));
3223         }
3224 
3225         /* Must not be world-writable. */
3226         if (st.st_mode & S_IWOTH) {
3227           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", skel_path,
3228             "' is world-writable", NULL));
3229         }
3230 
3231         /* Move the index past the skel parameter */
3232         i++;
3233 
3234       } else if (strcasecmp(cmd->argv[i], "dirmode") == 0) {
3235         char *tmp = NULL;
3236 
3237         dirmode = strtol(cmd->argv[++i], &tmp, 8);
3238 
3239         if (tmp && *tmp)
3240           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad mode parameter: '",
3241             cmd->argv[i], "'", NULL));
3242 
3243         /* Move the index past the dirmode parameter */
3244         i++;
3245 
3246       } else if (strcasecmp(cmd->argv[i], "uid") == 0) {
3247 
3248         /* Check for a "~" parameter. */
3249         if (strncmp(cmd->argv[i+1], "~", 2) != 0) {
3250           uid_t uid;
3251 
3252           if (pr_str2uid(cmd->argv[++i], &uid) < 0) {
3253             CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad UID parameter: '",
3254               cmd->argv[i], "'", NULL));
3255           }
3256 
3257           cuid = uid;
3258 
3259         } else {
3260           cuid = (uid_t) -1;
3261           i++;
3262         }
3263 
3264         /* Move the index past the uid parameter */
3265         i++;
3266 
3267       } else if (strcasecmp(cmd->argv[i], "gid") == 0) {
3268 
3269         /* Check for a "~" parameter. */
3270         if (strncmp(cmd->argv[i+1], "~", 2) != 0) {
3271           gid_t gid;
3272 
3273           if (pr_str2gid(cmd->argv[++i], &gid) < 0) {
3274             CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad GID parameter: '",
3275               cmd->argv[i], "'", NULL));
3276           }
3277 
3278           cgid = gid;
3279 
3280         } else {
3281           cgid = (gid_t) -1;
3282           i++;
3283         }
3284 
3285         /* Move the index past the gid parameter */
3286         i++;
3287 
3288       } else if (strcasecmp(cmd->argv[i], "homegid") == 0) {
3289         char *tmp = NULL;
3290         gid_t gid;
3291 
3292         gid = strtol(cmd->argv[++i], &tmp, 10);
3293 
3294         if (tmp && *tmp) {
3295           CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad GID parameter: '",
3296             cmd->argv[i], "'", NULL));
3297         }
3298 
3299         hgid = gid;
3300 
3301         /* Move the index past the homegid parameter */
3302         i++;
3303 
3304       } else if (strcasecmp(cmd->argv[i], "NoRootPrivs") == 0) {
3305         flags |= PR_MKHOME_FL_USE_USER_PRIVS;
3306         i++;
3307 
3308       } else {
3309         CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown parameter: '",
3310           cmd->argv[i], "'", NULL));
3311       }
3312     }
3313   }
3314 
3315   c = add_config_param(cmd->argv[0], 8, NULL, NULL, NULL, NULL,
3316     NULL, NULL, NULL, NULL);
3317 
3318   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3319   *((unsigned char *) c->argv[0]) = bool;
3320   c->argv[1] = pcalloc(c->pool, sizeof(mode_t));
3321   *((mode_t *) c->argv[1]) = mode;
3322   c->argv[2] = pcalloc(c->pool, sizeof(mode_t));
3323   *((mode_t *) c->argv[2]) = dirmode;
3324 
3325   if (skel_path) {
3326     c->argv[3] = pstrdup(c->pool, skel_path);
3327   }
3328 
3329   c->argv[4] = pcalloc(c->pool, sizeof(uid_t));
3330   *((uid_t *) c->argv[4]) = cuid;
3331   c->argv[5] = pcalloc(c->pool, sizeof(gid_t));
3332   *((gid_t *) c->argv[5]) = cgid;
3333   c->argv[6] = pcalloc(c->pool, sizeof(gid_t));
3334   *((gid_t *) c->argv[6]) = hgid;
3335   c->argv[7] = pcalloc(c->pool, sizeof(unsigned long));
3336   *((unsigned long *) c->argv[7]) = flags;
3337 
3338   return PR_HANDLED(cmd);
3339 }
3340 
add_defaultroot(cmd_rec * cmd)3341 MODRET add_defaultroot(cmd_rec *cmd) {
3342   config_rec *c;
3343   char *dir;
3344   unsigned int argc;
3345   void **argv;
3346   array_header *acl = NULL;
3347 
3348   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3349 
3350   if (cmd->argc < 2) {
3351     CONF_ERROR(cmd, "syntax: DefaultRoot <directory> [<group-expression>]");
3352   }
3353 
3354   argc = cmd->argc - 2;
3355   argv = cmd->argv;
3356 
3357   dir = *++argv;
3358 
3359   /* dir must be / or ~. */
3360   if (*dir != '/' &&
3361       *dir != '~') {
3362     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "(", dir, ") absolute pathname "
3363       "required", NULL));
3364   }
3365 
3366   if (strchr(dir, '*')) {
3367     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "(", dir, ") wildcards not allowed "
3368       "in pathname", NULL));
3369   }
3370 
3371   if (*(dir + strlen(dir) - 1) != '/') {
3372     dir = pstrcat(cmd->tmp_pool, dir, "/", NULL);
3373   }
3374 
3375   acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3376   c = add_config_param(cmd->argv[0], 0);
3377 
3378   c->argc = argc + 1;
3379   c->argv = pcalloc(c->pool, (argc + 2) * sizeof(void *));
3380   argv = c->argv;
3381   *argv++ = pstrdup(c->pool, dir);
3382 
3383   if (argc && acl)
3384     while(argc--) {
3385       *argv++ = pstrdup(c->pool, *((char **) acl->elts));
3386       acl->elts = ((char **) acl->elts) + 1;
3387     }
3388 
3389   *argv = NULL;
3390   return PR_HANDLED(cmd);
3391 }
3392 
add_defaultchdir(cmd_rec * cmd)3393 MODRET add_defaultchdir(cmd_rec *cmd) {
3394   config_rec *c;
3395   char *dir;
3396   unsigned int argc;
3397   void **argv;
3398   array_header *acl = NULL;
3399 
3400   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3401 
3402   if (cmd->argc < 2) {
3403     CONF_ERROR(cmd, "syntax: DefaultChdir <directory> [<group-expression>]");
3404   }
3405 
3406   argc = cmd->argc - 2;
3407   argv = cmd->argv;
3408 
3409   dir = *++argv;
3410 
3411   if (strchr(dir, '*')) {
3412     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "(", dir, ") wildcards not allowed "
3413       "in pathname", NULL));
3414   }
3415 
3416   if (*(dir + strlen(dir) - 1) != '/') {
3417     dir = pstrcat(cmd->tmp_pool, dir, "/", NULL);
3418   }
3419 
3420   acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3421   c = add_config_param(cmd->argv[0], 0);
3422 
3423   c->argc = argc + 1;
3424   c->argv = pcalloc(c->pool, (argc + 2) * sizeof(void *));
3425   argv = c->argv;
3426   *argv++ = pstrdup(c->pool, dir);
3427 
3428   if (argc && acl) {
3429     while(argc--) {
3430       *argv++ = pstrdup(c->pool, *((char **) acl->elts));
3431       acl->elts = ((char **) acl->elts) + 1;
3432     }
3433   }
3434 
3435   *argv = NULL;
3436 
3437   c->flags |= CF_MERGEDOWN;
3438   return PR_HANDLED(cmd);
3439 }
3440 
set_displaylogin(cmd_rec * cmd)3441 MODRET set_displaylogin(cmd_rec *cmd) {
3442   config_rec *c = NULL;
3443 
3444   CHECK_ARGS(cmd, 1);
3445   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3446 
3447   c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3448   c->flags |= CF_MERGEDOWN;
3449 
3450   return PR_HANDLED(cmd);
3451 }
3452 
3453 /* usage: MaxClientsPerClass class max|"none" ["message"] */
set_maxclientsclass(cmd_rec * cmd)3454 MODRET set_maxclientsclass(cmd_rec *cmd) {
3455   int max;
3456   config_rec *c;
3457 
3458   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3459 
3460   if (strcasecmp(cmd->argv[2], "none") == 0)
3461     max = 0;
3462 
3463   else {
3464     char *endp = NULL;
3465 
3466     max = (int) strtol(cmd->argv[2], &endp, 10);
3467 
3468     if ((endp && *endp) || max < 1)
3469       CONF_ERROR(cmd, "max must be 'none' or a number greater than 0");
3470   }
3471 
3472   if (cmd->argc == 4) {
3473     c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3474     c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
3475     c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3476     *((unsigned int *) c->argv[1]) = max;
3477     c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
3478 
3479   } else {
3480     c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3481     c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
3482     c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3483     *((unsigned int *) c->argv[1]) = max;
3484   }
3485 
3486   return PR_HANDLED(cmd);
3487 }
3488 
3489 /* usage: MaxClients max|"none" ["message"] */
set_maxclients(cmd_rec * cmd)3490 MODRET set_maxclients(cmd_rec *cmd) {
3491   int max;
3492   config_rec *c = NULL;
3493 
3494   if (cmd->argc < 2 || cmd->argc > 3)
3495     CONF_ERROR(cmd, "wrong number of parameters");
3496 
3497   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3498 
3499   if (!strcasecmp(cmd->argv[1], "none"))
3500     max = 0;
3501 
3502   else {
3503     char *endp = NULL;
3504 
3505     max = (int) strtol(cmd->argv[1], &endp, 10);
3506 
3507     if ((endp && *endp) || max < 1)
3508       CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
3509   }
3510 
3511   if (cmd->argc == 3) {
3512     c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3513     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3514     *((unsigned int *) c->argv[0]) = max;
3515     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
3516 
3517   } else {
3518     c = add_config_param(cmd->argv[0], 1, NULL);
3519     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3520     *((unsigned int *) c->argv[0]) = max;
3521   }
3522 
3523   c->flags |= CF_MERGEDOWN;
3524 
3525   return PR_HANDLED(cmd);
3526 }
3527 
3528 /* usage: MaxClientsPerHost max|"none" ["message"] */
set_maxhostclients(cmd_rec * cmd)3529 MODRET set_maxhostclients(cmd_rec *cmd) {
3530   int max;
3531   config_rec *c = NULL;
3532 
3533   if (cmd->argc < 2 || cmd->argc > 3)
3534     CONF_ERROR(cmd, "wrong number of parameters");
3535 
3536   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3537 
3538   if (!strcasecmp(cmd->argv[1], "none"))
3539     max = 0;
3540 
3541   else {
3542     char *endp = NULL;
3543 
3544     max = (int) strtol(cmd->argv[1], &endp, 10);
3545 
3546     if ((endp && *endp) || max < 1)
3547       CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
3548   }
3549 
3550   if (cmd->argc == 3) {
3551     c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3552     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3553     *((unsigned int *) c->argv[0]) = max;
3554     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
3555 
3556   } else {
3557     c = add_config_param(cmd->argv[0], 1, NULL);
3558     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3559     *((unsigned int *) c->argv[0]) = max;
3560   }
3561 
3562   c->flags |= CF_MERGEDOWN;
3563 
3564   return PR_HANDLED(cmd);
3565 }
3566 
3567 
3568 /* usage: MaxClientsPerUser max|"none" ["message"] */
set_maxuserclients(cmd_rec * cmd)3569 MODRET set_maxuserclients(cmd_rec *cmd) {
3570   int max;
3571   config_rec *c = NULL;
3572 
3573   if (cmd->argc < 2 || cmd->argc > 3)
3574     CONF_ERROR(cmd, "wrong number of parameters");
3575 
3576   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3577 
3578   if (!strcasecmp(cmd->argv[1], "none"))
3579     max = 0;
3580 
3581   else {
3582     char *endp = NULL;
3583 
3584     max = (int) strtol(cmd->argv[1], &endp, 10);
3585 
3586     if ((endp && *endp) || max < 1)
3587       CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
3588   }
3589 
3590   if (cmd->argc == 3) {
3591     c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3592     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3593     *((unsigned int *) c->argv[0]) = max;
3594     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
3595 
3596   } else {
3597     c = add_config_param(cmd->argv[0], 1, NULL);
3598     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3599     *((unsigned int *) c->argv[0]) = max;
3600   }
3601 
3602   c->flags |= CF_MERGEDOWN;
3603 
3604   return PR_HANDLED(cmd);
3605 }
3606 
3607 /* usage: MaxConnectionsPerHost max|"none" ["message"] */
set_maxconnectsperhost(cmd_rec * cmd)3608 MODRET set_maxconnectsperhost(cmd_rec *cmd) {
3609   int max;
3610   config_rec *c;
3611 
3612   if (cmd->argc < 2 || cmd->argc > 3)
3613     CONF_ERROR(cmd, "wrong number of parameters");
3614 
3615   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3616 
3617   if (strcasecmp(cmd->argv[1], "none") == 0)
3618     max = 0;
3619 
3620   else {
3621     char *tmp = NULL;
3622 
3623     max = (int) strtol(cmd->argv[1], &tmp, 10);
3624 
3625     if ((tmp && *tmp) || max < 1)
3626       CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
3627   }
3628 
3629   if (cmd->argc == 3) {
3630     c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3631     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
3632 
3633   } else
3634     c = add_config_param(cmd->argv[0], 1, NULL);
3635 
3636   c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3637   *((unsigned int *) c->argv[0]) = max;
3638 
3639   return PR_HANDLED(cmd);
3640 }
3641 
3642 /* usage: MaxHostsPerUser max|"none" ["message"] */
set_maxhostsperuser(cmd_rec * cmd)3643 MODRET set_maxhostsperuser(cmd_rec *cmd) {
3644   int max;
3645   config_rec *c = NULL;
3646 
3647   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3648 
3649   if (cmd->argc < 2 || cmd->argc > 3)
3650     CONF_ERROR(cmd, "wrong number of parameters");
3651 
3652   if (!strcasecmp(cmd->argv[1], "none"))
3653     max = 0;
3654 
3655   else {
3656     char *endp = NULL;
3657 
3658     max = (int) strtol(cmd->argv[1], &endp, 10);
3659 
3660     if ((endp && *endp) || max < 1)
3661       CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
3662   }
3663 
3664   if (cmd->argc == 3) {
3665     c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3666     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3667     *((unsigned int *) c->argv[0]) = max;
3668     c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
3669 
3670   } else {
3671     c = add_config_param(cmd->argv[0], 1, NULL);
3672     c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3673     *((unsigned int *) c->argv[0]) = max;
3674   }
3675 
3676   c->flags |= CF_MERGEDOWN;
3677 
3678   return PR_HANDLED(cmd);
3679 }
3680 
set_maxloginattempts(cmd_rec * cmd)3681 MODRET set_maxloginattempts(cmd_rec *cmd) {
3682   int max;
3683   config_rec *c = NULL;
3684 
3685   CHECK_ARGS(cmd, 1);
3686   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3687 
3688   if (strcasecmp(cmd->argv[1], "none") == 0) {
3689     max = 0;
3690 
3691   } else {
3692     char *endp = NULL;
3693     max = (int) strtol(cmd->argv[1], &endp, 10);
3694 
3695     if ((endp && *endp) || max < 1)
3696       CONF_ERROR(cmd, "parameter must be 'none' or a number greater than 0");
3697   }
3698 
3699   c = add_config_param(cmd->argv[0], 1, NULL);
3700   c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
3701   *((unsigned int *) c->argv[0]) = max;
3702 
3703   return PR_HANDLED(cmd);
3704 }
3705 
3706 /* usage: MaxPasswordSize len */
set_maxpasswordsize(cmd_rec * cmd)3707 MODRET set_maxpasswordsize(cmd_rec *cmd) {
3708   config_rec *c;
3709   size_t password_len;
3710   char *len, *ptr = NULL;
3711 
3712   CHECK_ARGS(cmd, 1);
3713   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3714 
3715   len = cmd->argv[1];
3716   if (*len == '-') {
3717     CONF_ERROR(cmd, "badly formatted parameter");
3718   }
3719 
3720   password_len = strtoul(len, &ptr, 10);
3721   if (ptr && *ptr) {
3722     CONF_ERROR(cmd, "badly formatted parameter");
3723   }
3724 
3725 /* XXX Applies to the following modules, which use crypt(3):
3726  *
3727  *  mod_ldap (ldap_auth_check; "check" authtab)
3728  *    ldap_auth_auth ("auth" authtab) calls pr_auth_check()
3729  *  mod_sql (sql_auth_crypt, via SQLAuthTypes; cmd_check "check" authtab dispatches here)
3730  *    cmd_auth ("auth" authtab) calls pr_auth_check()
3731  *  mod_auth_file (authfile_chkpass, "check" authtab)
3732  *    authfile_auth ("auth" authtab) calls pr_auth_check()
3733  *  mod_auth_unix (pw_check, "check" authtab)
3734  *    pw_auth ("auth" authtab) calls pr_auth_check()
3735  *
3736  *  mod_sftp uses pr_auth_authenticate(), which will dispatch into above
3737  *
3738  *  mod_radius does NOT use either -- up to RADIUS server policy?
3739  *
3740  * Is there a common code path that all of the above go through?
3741  */
3742 
3743   c = add_config_param(cmd->argv[0], 1, NULL);
3744   c->argv[0] = palloc(c->pool, sizeof(size_t));
3745   *((size_t *) c->argv[0]) = password_len;
3746 
3747   return PR_HANDLED(cmd);
3748 }
3749 
set_requirevalidshell(cmd_rec * cmd)3750 MODRET set_requirevalidshell(cmd_rec *cmd) {
3751   int bool = -1;
3752   config_rec *c = NULL;
3753 
3754   CHECK_ARGS(cmd, 1);
3755   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3756 
3757   bool = get_boolean(cmd, 1);
3758   if (bool == -1)
3759     CONF_ERROR(cmd, "expected Boolean parameter");
3760 
3761   c = add_config_param(cmd->argv[0], 1, NULL);
3762   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3763   *((unsigned char *) c->argv[0]) = bool;
3764   c->flags |= CF_MERGEDOWN;
3765 
3766   return PR_HANDLED(cmd);
3767 }
3768 
3769 /* usage: RewriteHome on|off */
set_rewritehome(cmd_rec * cmd)3770 MODRET set_rewritehome(cmd_rec *cmd) {
3771   int bool = -1;
3772   config_rec *c = NULL;
3773 
3774   CHECK_ARGS(cmd, 1);
3775   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3776 
3777   bool = get_boolean(cmd, 1);
3778   if (bool == -1)
3779     CONF_ERROR(cmd, "expected Boolean parameter");
3780 
3781   c = add_config_param(cmd->argv[0], 1, NULL);
3782   c->argv[0] = pcalloc(c->pool, sizeof(int));
3783   *((int *) c->argv[0]) = bool;
3784 
3785   return PR_HANDLED(cmd);
3786 }
3787 
set_rootlogin(cmd_rec * cmd)3788 MODRET set_rootlogin(cmd_rec *cmd) {
3789   int bool = -1;
3790   config_rec *c = NULL;
3791 
3792   CHECK_ARGS(cmd,1);
3793   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3794 
3795   bool = get_boolean(cmd, 1);
3796   if (bool == -1)
3797     CONF_ERROR(cmd, "expected Boolean parameter");
3798 
3799   c = add_config_param(cmd->argv[0], 1, NULL);
3800   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3801   *((unsigned char *) c->argv[0]) = (unsigned char) bool;
3802   c->flags |= CF_MERGEDOWN;
3803 
3804   return PR_HANDLED(cmd);
3805 }
3806 
3807 /* usage: RootRevoke on|off|UseNonCompliantActiveTransfer */
set_rootrevoke(cmd_rec * cmd)3808 MODRET set_rootrevoke(cmd_rec *cmd) {
3809   int root_revoke = -1;
3810   config_rec *c = NULL;
3811 
3812   CHECK_ARGS(cmd, 1);
3813   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3814 
3815   /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and
3816    * 2 indicates 'NonCompliantActiveTransfer'.
3817    */
3818   root_revoke = get_boolean(cmd, 1);
3819   if (root_revoke == -1) {
3820     if (strcasecmp(cmd->argv[1], "UseNonCompliantActiveTransfer") != 0 &&
3821         strcasecmp(cmd->argv[1], "UseNonCompliantActiveTransfers") != 0) {
3822       CONF_ERROR(cmd, "expected Boolean parameter");
3823     }
3824 
3825     root_revoke = 2;
3826   }
3827 
3828   c = add_config_param(cmd->argv[0], 1, NULL);
3829   c->argv[0] = pcalloc(c->pool, sizeof(int));
3830   *((int *) c->argv[0]) = root_revoke;
3831 
3832   c->flags |= CF_MERGEDOWN;
3833   return PR_HANDLED(cmd);
3834 }
3835 
set_timeoutlogin(cmd_rec * cmd)3836 MODRET set_timeoutlogin(cmd_rec *cmd) {
3837   int timeout = -1;
3838   config_rec *c = NULL;
3839 
3840   CHECK_ARGS(cmd, 1);
3841   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3842 
3843   if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
3844     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3845       cmd->argv[1], "': ", strerror(errno), NULL));
3846   }
3847 
3848   c = add_config_param(cmd->argv[0], 1, NULL);
3849   c->argv[0] = pcalloc(c->pool, sizeof(int));
3850   *((int *) c->argv[0]) = timeout;
3851 
3852   return PR_HANDLED(cmd);
3853 }
3854 
set_timeoutsession(cmd_rec * cmd)3855 MODRET set_timeoutsession(cmd_rec *cmd) {
3856   int timeout = 0, precedence = 0;
3857   config_rec *c = NULL;
3858 
3859   int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
3860      cmd->config->config_type : cmd->server->config_type ?
3861      cmd->server->config_type : CONF_ROOT);
3862 
3863   /* this directive must have either 1 or 3 arguments */
3864   if (cmd->argc-1 != 1 &&
3865       cmd->argc-1 != 3) {
3866     CONF_ERROR(cmd, "missing parameters");
3867   }
3868 
3869   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3870 
3871   /* Set the precedence for this config_rec based on its configuration
3872    * context.
3873    */
3874   if (ctxt & CONF_GLOBAL) {
3875     precedence = 1;
3876 
3877   /* These will never appear simultaneously */
3878   } else if ((ctxt & CONF_ROOT) ||
3879              (ctxt & CONF_VIRTUAL)) {
3880     precedence = 2;
3881 
3882   } else if (ctxt & CONF_ANON) {
3883     precedence = 3;
3884   }
3885 
3886   if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
3887     CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3888       cmd->argv[1], "': ", strerror(errno), NULL));
3889   }
3890 
3891   if (timeout == 0) {
3892     /* do nothing */
3893     return PR_HANDLED(cmd);
3894   }
3895 
3896   if (cmd->argc-1 == 3) {
3897     if (strncmp(cmd->argv[2], "user", 5) == 0 ||
3898         strncmp(cmd->argv[2], "group", 6) == 0 ||
3899         strncmp(cmd->argv[2], "class", 6) == 0) {
3900 
3901        /* no op */
3902 
3903      } else {
3904        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, cmd->argv[0],
3905          ": unknown classifier used: '", cmd->argv[2], "'", NULL));
3906     }
3907   }
3908 
3909   if (cmd->argc-1 == 1) {
3910     c = add_config_param(cmd->argv[0], 2, NULL);
3911     c->argv[0] = pcalloc(c->pool, sizeof(int));
3912     *((int *) c->argv[0]) = timeout;
3913     c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3914     *((unsigned int *) c->argv[1]) = precedence;
3915 
3916   } else if (cmd->argc-1 == 3) {
3917     array_header *acl = NULL;
3918     unsigned int argc;
3919     void **argv;
3920 
3921     argc = cmd->argc - 3;
3922     argv = cmd->argv + 2;
3923 
3924     acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3925 
3926     c = add_config_param(cmd->argv[0], 0);
3927     c->argc = argc + 2;
3928 
3929     /* Add 3 to argc for the argv of the config_rec: one for the
3930      * seconds value, one for the precedence, one for the classifier,
3931      * and one for the terminating NULL.
3932      */
3933     c->argv = pcalloc(c->pool, ((argc + 4) * sizeof(void *)));
3934 
3935     /* Capture the config_rec's argv pointer for doing the by-hand
3936      * population.
3937      */
3938     argv = c->argv;
3939 
3940     /* Copy in the seconds. */
3941     *argv = pcalloc(c->pool, sizeof(int));
3942     *((int *) *argv++) = timeout;
3943 
3944     /* Copy in the precedence. */
3945     *argv = pcalloc(c->pool, sizeof(unsigned int));
3946     *((unsigned int *) *argv++) = precedence;
3947 
3948     /* Copy in the classifier. */
3949     *argv++ = pstrdup(c->pool, cmd->argv[2]);
3950 
3951     /* now, copy in the expression arguments */
3952     if (argc && acl) {
3953       while (argc--) {
3954         *argv++ = pstrdup(c->pool, *((char **) acl->elts));
3955         acl->elts = ((char **) acl->elts) + 1;
3956       }
3957     }
3958 
3959     /* don't forget the terminating NULL */
3960     *argv = NULL;
3961 
3962   } else {
3963     /* Should never reach here. */
3964     CONF_ERROR(cmd, "wrong number of parameters");
3965   }
3966 
3967   c->flags |= CF_MERGEDOWN_MULTI;
3968   return PR_HANDLED(cmd);
3969 }
3970 
set_useftpusers(cmd_rec * cmd)3971 MODRET set_useftpusers(cmd_rec *cmd) {
3972   int bool = -1;
3973   config_rec *c = NULL;
3974 
3975   CHECK_ARGS(cmd, 1);
3976   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3977 
3978   bool = get_boolean(cmd, 1);
3979   if (bool == -1)
3980     CONF_ERROR(cmd, "expected Boolean parameter");
3981 
3982   c = add_config_param(cmd->argv[0], 1, NULL);
3983   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3984   *((unsigned char *) c->argv[0]) = bool;
3985   c->flags |= CF_MERGEDOWN;
3986 
3987   return PR_HANDLED(cmd);
3988 }
3989 
3990 /* usage: UseLastlog on|off */
set_uselastlog(cmd_rec * cmd)3991 MODRET set_uselastlog(cmd_rec *cmd) {
3992 #ifdef PR_USE_LASTLOG
3993   int bool;
3994   config_rec *c;
3995 
3996   CHECK_ARGS(cmd, 1);
3997   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3998 
3999   bool = get_boolean(cmd, 1);
4000   if (bool == -1)
4001     CONF_ERROR(cmd, "expected Boolean parameter");
4002 
4003   c = add_config_param(cmd->argv[0], 1, NULL);
4004   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
4005   *((unsigned char *) c->argv[0]) = bool;
4006 
4007   return PR_HANDLED(cmd);
4008 #else
4009   CONF_ERROR(cmd, "requires lastlog support (--with-lastlog)");
4010 #endif /* PR_USE_LASTLOG */
4011 }
4012 
4013 /* usage: UserAlias alias real-user */
set_useralias(cmd_rec * cmd)4014 MODRET set_useralias(cmd_rec *cmd) {
4015   config_rec *c = NULL;
4016   char *alias, *real_user;
4017 
4018   CHECK_ARGS(cmd, 2);
4019   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
4020 
4021   /* Make sure that the given names differ. */
4022   alias = cmd->argv[1];
4023   real_user = cmd->argv[2];
4024 
4025   if (strcmp(alias, real_user) == 0) {
4026     CONF_ERROR(cmd, "alias and real user names must differ");
4027   }
4028 
4029   c = add_config_param_str(cmd->argv[0], 2, alias, real_user);
4030 
4031   /* Note: only merge this directive down if it is not appearing in an
4032    * <Anonymous> context.
4033    */
4034   if (!check_context(cmd, CONF_ANON)) {
4035     c->flags |= CF_MERGEDOWN_MULTI;
4036   }
4037 
4038   return PR_HANDLED(cmd);
4039 }
4040 
set_userdirroot(cmd_rec * cmd)4041 MODRET set_userdirroot(cmd_rec *cmd) {
4042   int bool = -1;
4043   config_rec *c = NULL;
4044 
4045   CHECK_ARGS(cmd, 1);
4046   CHECK_CONF(cmd, CONF_ANON);
4047 
4048   bool = get_boolean(cmd, 1);
4049   if (bool == -1)
4050     CONF_ERROR(cmd, "expected Boolean parameter");
4051 
4052   c = add_config_param(cmd->argv[0], 1, NULL);
4053   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
4054   *((unsigned char *) c->argv[0]) = bool;
4055 
4056   return PR_HANDLED(cmd);
4057 }
4058 
set_userpassword(cmd_rec * cmd)4059 MODRET set_userpassword(cmd_rec *cmd) {
4060   config_rec *c = NULL;
4061 
4062   CHECK_ARGS(cmd, 2);
4063   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
4064 
4065   c = add_config_param_str(cmd->argv[0], 2, cmd->argv[1], cmd->argv[2]);
4066   c->flags |= CF_MERGEDOWN;
4067 
4068   return PR_HANDLED(cmd);
4069 }
4070 
4071 /* usage: WtmpLog on|off */
set_wtmplog(cmd_rec * cmd)4072 MODRET set_wtmplog(cmd_rec *cmd) {
4073   int use_wtmp = -1;
4074   config_rec *c = NULL;
4075 
4076   CHECK_ARGS(cmd, 1);
4077   CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
4078 
4079   if (strcasecmp(cmd->argv[1], "NONE") == 0) {
4080     use_wtmp = FALSE;
4081 
4082   } else {
4083     use_wtmp = get_boolean(cmd, 1);
4084     if (use_wtmp == -1) {
4085       CONF_ERROR(cmd, "expected Boolean parameter");
4086     }
4087   }
4088 
4089   c = add_config_param(cmd->argv[0], 1, NULL);
4090   c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
4091   *((unsigned char *) c->argv[0]) = use_wtmp;
4092   c->flags |= CF_MERGEDOWN;
4093 
4094   return PR_HANDLED(cmd);
4095 }
4096 
4097 /* Module API tables
4098  */
4099 
4100 static conftable auth_conftab[] = {
4101   { "AccessDenyMsg",		set_accessdenymsg,		NULL },
4102   { "AccessGrantMsg",		set_accessgrantmsg,		NULL },
4103   { "AllowChrootSymlinks",	set_allowchrootsymlinks,	NULL },
4104   { "AllowEmptyPasswords",	set_allowemptypasswords,	NULL },
4105   { "AnonAllowRobots",		set_anonallowrobots,		NULL },
4106   { "AnonRequirePassword",	set_anonrequirepassword,	NULL },
4107   { "AnonRejectPasswords",	set_anonrejectpasswords,	NULL },
4108   { "AuthAliasOnly",		set_authaliasonly,		NULL },
4109   { "AuthUsingAlias",		set_authusingalias,		NULL },
4110   { "CreateHome",		set_createhome,			NULL },
4111   { "DefaultChdir",		add_defaultchdir,		NULL },
4112   { "DefaultRoot",		add_defaultroot,		NULL },
4113   { "DisplayLogin",		set_displaylogin,		NULL },
4114   { "MaxClients",		set_maxclients,			NULL },
4115   { "MaxClientsPerClass",	set_maxclientsclass,		NULL },
4116   { "MaxClientsPerHost",	set_maxhostclients,		NULL },
4117   { "MaxClientsPerUser",	set_maxuserclients,		NULL },
4118   { "MaxConnectionsPerHost",	set_maxconnectsperhost,		NULL },
4119   { "MaxHostsPerUser",		set_maxhostsperuser,		NULL },
4120   { "MaxLoginAttempts",		set_maxloginattempts,		NULL },
4121   { "MaxPasswordSize",		set_maxpasswordsize,		NULL },
4122   { "RequireValidShell",	set_requirevalidshell,		NULL },
4123   { "RewriteHome",		set_rewritehome,		NULL },
4124   { "RootLogin",		set_rootlogin,			NULL },
4125   { "RootRevoke",		set_rootrevoke,			NULL },
4126   { "TimeoutLogin",		set_timeoutlogin,		NULL },
4127   { "TimeoutSession",		set_timeoutsession,		NULL },
4128   { "UseFtpUsers",		set_useftpusers,		NULL },
4129   { "UseLastlog",		set_uselastlog,			NULL },
4130   { "UserAlias",		set_useralias,			NULL },
4131   { "UserDirRoot",		set_userdirroot,		NULL },
4132   { "UserPassword",		set_userpassword,		NULL },
4133   { "WtmpLog",			set_wtmplog,			NULL },
4134 
4135   { NULL,			NULL,				NULL }
4136 };
4137 
4138 static cmdtable auth_cmdtab[] = {
4139   { PRE_CMD,	C_USER,	G_NONE,	auth_pre_user,	FALSE,	FALSE,	CL_AUTH },
4140   { CMD,	C_USER,	G_NONE,	auth_user,	FALSE,	FALSE,	CL_AUTH },
4141   { PRE_CMD,	C_PASS,	G_NONE,	auth_pre_pass,	FALSE,	FALSE,	CL_AUTH },
4142   { CMD,	C_PASS,	G_NONE,	auth_pass,	FALSE,	FALSE,	CL_AUTH },
4143   { POST_CMD,	C_PASS,	G_NONE,	auth_post_pass,	FALSE,	FALSE,	CL_AUTH },
4144   { LOG_CMD,	C_PASS,	G_NONE,	auth_log_pass,  FALSE,  FALSE },
4145   { LOG_CMD_ERR,C_PASS,	G_NONE,	auth_err_pass,  FALSE,  FALSE },
4146   { CMD,	C_ACCT,	G_NONE,	auth_acct,	FALSE,	FALSE,	CL_AUTH },
4147   { CMD,	C_REIN,	G_NONE,	auth_rein,	FALSE,	FALSE,	CL_AUTH },
4148 
4149   /* For the automatic robots.txt handling */
4150   { PRE_CMD,	C_RETR,	G_NONE,	auth_pre_retr,	FALSE,	FALSE },
4151   { POST_CMD,	C_RETR,	G_NONE,	auth_post_retr,	FALSE,	FALSE },
4152   { POST_CMD_ERR,C_RETR,G_NONE,	auth_post_retr,	FALSE,	FALSE },
4153 
4154   { 0, NULL }
4155 };
4156 
4157 /* Module interface */
4158 
4159 module auth_module = {
4160   NULL, NULL,
4161 
4162   /* Module API version */
4163   0x20,
4164 
4165   /* Module name */
4166   "auth",
4167 
4168   /* Module configuration directive table */
4169   auth_conftab,
4170 
4171   /* Module command handler table */
4172   auth_cmdtab,
4173 
4174   /* Module authentication handler table */
4175   NULL,
4176 
4177   /* Module initialization function */
4178   auth_init,
4179 
4180   /* Session initialization function */
4181   auth_sess_init
4182 };
4183 
4184