Coverage Report

Created: 2026-08-15 07:23

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