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