Coverage Report

Created: 2024-09-30 06:24

/src/proftpd/modules/mod_auth_unix.c
Line
Count
Source (jump to first uncovered line)
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-2023 The ProFTPD Project team
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20
 *
21
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22
 * and other respective copyright holders give permission to link this program
23
 * with OpenSSL, and distribute the resulting executable, without including
24
 * the source code for OpenSSL in the source distribution.
25
 */
26
27
/* Unix authentication module for ProFTPD */
28
29
#include "conf.h"
30
31
/* AIX has some rather stupid function prototype inconsistencies between
32
 * their crypt.h and stdlib.h's setkey() declarations.  *sigh*
33
 */
34
#if defined(HAVE_CRYPT_H) && !defined(AIX4) && !defined(AIX5)
35
# include <crypt.h>
36
#endif
37
38
#ifdef PR_USE_SHADOW
39
# ifdef HAVE_SHADOW_H
40
#   include <shadow.h>
41
# endif
42
#endif
43
44
#ifdef HAVE_SYS_SECURITY_H
45
# include <sys/security.h>
46
#endif
47
48
#ifdef HAVE_KRB_H
49
# include <krb.h>
50
#endif
51
52
#ifdef HAVE_LOGIN_H
53
# include <login.h>
54
#endif
55
56
#if defined(HAVE_HPSECURITY_H) || defined(HPUX10) || defined(HPUX11)
57
# include <hpsecurity.h>
58
# ifndef COMSEC
59
#  define COMSEC 1
60
# endif /* !COMSEC */
61
#endif /* HAVE_HPSECURITY_H or HPUX10 or HPUX11 */
62
63
#if defined(HAVE_PROT_H) || defined(COMSEC)
64
# include <prot.h>
65
#endif
66
67
#ifdef HAVE_USERSEC_H
68
# include <usersec.h>
69
#endif
70
71
#ifdef PR_USE_SIA
72
# ifdef HAVE_SIA_H
73
#  include <sia.h>
74
# endif
75
# ifdef HAVE_SIAD_H
76
#  include <siad.h>
77
# endif
78
#endif /* PR_USE_SIA */
79
80
#ifdef CYGWIN
81
typedef void *HANDLE;
82
typedef unsigned long DWORD;
83
# define INVALID_HANDLE_VALUE (HANDLE)(-1)
84
# define WINAPI __stdcall
85
DWORD WINAPI GetVersion(void);
86
extern HANDLE cygwin_logon_user (const struct passwd *, const char *);
87
extern void cygwin_set_impersonation_token (const HANDLE);
88
#endif /* CYGWIN */
89
90
#ifdef SETGRENT_VOID
91
0
# define RETSETGRENTTYPE  void
92
#else
93
# define RETSETGRENTTYPE  int
94
#endif
95
96
#include "privs.h"
97
98
#ifdef HAVE__PW_STAYOPEN
99
extern int _pw_stayopen;
100
#endif
101
102
module auth_unix_module;
103
104
static const char *pwdfname = "/etc/passwd";
105
static FILE *pwdf = NULL;
106
107
static const char *grpfname = "/etc/group";
108
static FILE *grpf = NULL;
109
110
static int unix_persistent_passwd = FALSE;
111
static const char *trace_channel = "auth.unix";
112
113
#undef PASSWD
114
0
#define PASSWD    pwdfname
115
#undef GROUP
116
0
#define GROUP   grpfname
117
118
#ifdef PR_USE_SHADOW
119
120
/* Shadow password entries are stored as number of days, not seconds
121
 * and are -1 if unused
122
 */
123
0
#define SP_CVT_DAYS(x)  ((x) == (time_t)-1 ? (x) : ((x) * 86400))
124
125
#endif /* PR_USE_SHADOW */
126
127
/* mod_auth_unix option flags */
128
0
#define AUTH_UNIX_OPT_AIX_NO_RLOGIN   0x0001
129
0
#define AUTH_UNIX_OPT_NO_GETGROUPLIST   0x0002
130
0
#define AUTH_UNIX_OPT_MAGIC_TOKEN_CHROOT  0x0004
131
0
#define AUTH_UNIX_OPT_NO_INITGROUPS   0x0008
132
0
#define AUTH_UNIX_OPT_AIX_NO_AUTHENTICATE 0x0010
133
134
static unsigned long auth_unix_opts = 0UL;
135
136
/* Necessary prototypes */
137
static void auth_unix_exit_ev(const void *, void *);
138
static int auth_unix_sess_init(void);
139
140
0
static void p_setpwent(void) {
141
0
  if (pwdf != NULL) {
142
0
    rewind(pwdf);
143
144
0
  } else {
145
0
    pwdf = fopen(PASSWD, "r");
146
0
    if (pwdf == NULL) {
147
0
      pr_log_pri(PR_LOG_ERR, "unable to open password file %s for reading: %s",
148
0
        PASSWD, strerror(errno));
149
0
    }
150
0
  }
151
0
}
152
153
0
static void p_endpwent(void) {
154
0
  if (pwdf != NULL) {
155
0
    fclose(pwdf);
156
0
    pwdf = NULL;
157
0
  }
158
0
}
159
160
0
static RETSETGRENTTYPE p_setgrent(void) {
161
0
  if (grpf != NULL) {
162
0
    rewind(grpf);
163
164
0
  } else {
165
0
    grpf = fopen(GROUP, "r");
166
0
    if (grpf == NULL) {
167
0
      pr_log_pri(PR_LOG_ERR, "unable to open group file %s for reading: %s",
168
0
        GROUP, strerror(errno));
169
0
    }
170
0
  }
171
172
#ifndef SETGRENT_VOID
173
  return 0;
174
#endif
175
0
}
176
177
0
static void p_endgrent(void) {
178
0
  if (grpf != NULL) {
179
0
    fclose(grpf);
180
0
    grpf = NULL;
181
0
  }
182
0
}
183
184
0
static struct passwd *p_getpwent(void) {
185
0
  if (pwdf == NULL) {
186
0
    p_setpwent();
187
0
  }
188
189
0
  if (pwdf == NULL) {
190
0
    return NULL;
191
0
  }
192
193
0
  return fgetpwent(pwdf);
194
0
}
195
196
0
static struct group *p_getgrent(void) {
197
0
  if (grpf == NULL) {
198
0
    p_setgrent();
199
0
  }
200
201
0
  if (grpf == NULL) {
202
0
    return NULL;
203
0
  }
204
205
0
  return fgetgrent(grpf);
206
0
}
207
208
0
static struct passwd *p_getpwnam(const char *name) {
209
0
  struct passwd *pw = NULL;
210
0
  size_t name_len;
211
212
0
  p_setpwent();
213
0
  name_len = strlen(name);
214
215
0
  while ((pw = p_getpwent()) != NULL) {
216
0
    pr_signals_handle();
217
218
0
    if (strncmp(name, pw->pw_name, name_len + 1) == 0) {
219
0
      break;
220
0
    }
221
0
  }
222
223
0
  return pw;
224
0
}
225
226
0
static struct passwd *p_getpwuid(uid_t uid) {
227
0
  struct passwd *pw = NULL;
228
229
0
  p_setpwent();
230
0
  while ((pw = p_getpwent()) != NULL) {
231
0
    pr_signals_handle();
232
233
0
    if (pw->pw_uid == uid) {
234
0
      break;
235
0
    }
236
0
  }
237
238
0
  return pw;
239
0
}
240
241
0
static struct group *p_getgrnam(const char *name) {
242
0
  struct group *gr = NULL;
243
0
  size_t name_len;
244
245
0
  p_setgrent();
246
0
  name_len = strlen(name);
247
248
0
  while ((gr = p_getgrent()) != NULL) {
249
0
    pr_signals_handle();
250
251
0
    if (strncmp(name, gr->gr_name, name_len + 1) == 0) {
252
0
      break;
253
0
    }
254
0
  }
255
256
0
  return gr;
257
0
}
258
259
0
static struct group *p_getgrgid(gid_t gid) {
260
0
  struct group *gr = NULL;
261
262
0
  p_setgrent();
263
0
  while ((gr = p_getgrent()) != NULL) {
264
0
    pr_signals_handle();
265
266
0
    if (gr->gr_gid == gid) {
267
0
      break;
268
0
    }
269
0
  }
270
271
0
  return gr;
272
0
}
273
274
0
MODRET pw_setpwent(cmd_rec *cmd) {
275
0
  if (unix_persistent_passwd) {
276
0
    p_setpwent();
277
278
0
  } else {
279
0
    setpwent();
280
0
  }
281
282
0
  return PR_DECLINED(cmd);
283
0
}
284
285
0
MODRET pw_endpwent(cmd_rec *cmd) {
286
0
  if (unix_persistent_passwd) {
287
0
    p_endpwent();
288
289
0
  } else {
290
0
    endpwent();
291
0
  }
292
293
0
  return PR_DECLINED(cmd);
294
0
}
295
296
0
MODRET pw_setgrent(cmd_rec *cmd) {
297
0
  if (unix_persistent_passwd) {
298
0
    p_setgrent();
299
300
0
  } else {
301
0
    setgrent();
302
0
  }
303
304
0
  return PR_DECLINED(cmd);
305
0
}
306
307
0
MODRET pw_endgrent(cmd_rec *cmd) {
308
0
  if (unix_persistent_passwd) {
309
0
    p_endgrent();
310
311
0
  } else {
312
0
    endgrent();
313
0
  }
314
315
0
  return PR_DECLINED(cmd);
316
0
}
317
318
0
MODRET pw_getgrent(cmd_rec *cmd) {
319
0
  struct group *gr = NULL;
320
321
0
  if (unix_persistent_passwd) {
322
0
    gr = p_getgrent();
323
324
0
  } else {
325
0
    gr = getgrent();
326
0
  }
327
328
0
  return gr ? mod_create_data(cmd, gr) : PR_DECLINED(cmd);
329
0
}
330
331
0
MODRET pw_getpwent(cmd_rec *cmd) {
332
0
  struct passwd *pw = NULL;
333
334
0
  if (unix_persistent_passwd) {
335
0
    pw = p_getpwent();
336
337
0
  } else {
338
0
    pw = getpwent();
339
0
  }
340
341
0
  return pw ? mod_create_data(cmd, pw) : PR_DECLINED(cmd);
342
0
}
343
344
0
MODRET pw_getpwuid(cmd_rec *cmd) {
345
0
  struct passwd *pw = NULL;
346
0
  uid_t uid;
347
348
0
  uid = *((uid_t *) cmd->argv[0]);
349
0
  if (unix_persistent_passwd) {
350
0
    pw = p_getpwuid(uid);
351
352
0
  } else {
353
0
    pw = getpwuid(uid);
354
0
  }
355
356
0
  return pw ? mod_create_data(cmd, pw) : PR_DECLINED(cmd);
357
0
}
358
359
0
MODRET pw_getpwnam(cmd_rec *cmd) {
360
0
  struct passwd *pw = NULL;
361
0
  const char *name;
362
363
0
  name = cmd->argv[0];
364
0
  if (unix_persistent_passwd) {
365
0
    pw = p_getpwnam(name);
366
367
0
  } else {
368
0
    pw = getpwnam(name);
369
0
  }
370
371
0
  if (pw == NULL) {
372
0
    return PR_DECLINED(cmd);
373
0
  }
374
375
0
  if (auth_unix_opts & AUTH_UNIX_OPT_MAGIC_TOKEN_CHROOT) {
376
0
    char *home_dir, *ptr;
377
378
    /* Here is where we do the "magic token" chroot monstrosity inflicted
379
     * on the world by wu-ftpd.
380
     *
381
     * If the magic token '/./' appears in the user's home directory, the
382
     * directory portion before the token is the directory to use for
383
     * the chroot; the directory portion after the token is the directory
384
     * to use for the initial chdir.
385
     */
386
387
0
    home_dir = pstrdup(cmd->tmp_pool, pw->pw_dir);
388
389
    /* We iterate through the home directory string since it is possible
390
     * for the '.' character to appear without it being part of the magic
391
     * token.
392
     */
393
0
    ptr = strchr(home_dir, '.');
394
0
    while (ptr != NULL) {
395
0
      pr_signals_handle();
396
397
      /* If we're at the start of the home directory string, stop looking:
398
       * this home directory is not really valid anyway.
399
       */
400
0
      if (ptr == home_dir) {
401
0
        break;
402
0
      }
403
404
      /* Back up one character. */
405
0
      ptr--;
406
407
      /* If we're at the start of the home directory now, stop looking:
408
       * this home directory cannot contain a valid magic token.  I.e.
409
       *
410
       * /./home/foo
411
       *
412
       * cannot be valid, as there is no directory portion before the
413
       * token.
414
       */
415
0
      if (ptr == home_dir) {
416
0
        break;
417
0
      }
418
419
0
      if (strncmp(ptr, "/./", 3) == 0) {
420
0
        char *default_chdir;
421
0
        config_rec *c;
422
423
0
        *ptr = '\0';
424
0
        default_chdir = pstrdup(cmd->tmp_pool, ptr + 2);
425
426
        /* In order to make sure that this user is chrooted to this
427
         * directory, we remove all DefaultRoot directives and add a new
428
         * one.  Same for the DefaultChdir directive.
429
         */
430
431
0
        (void) remove_config(main_server->conf, "DefaultRoot", FALSE);
432
0
        c = add_config_param_set(&main_server->conf, "DefaultRoot", 1, NULL);
433
0
        c->argv[0] = pstrdup(c->pool, home_dir);
434
435
0
        (void) remove_config(main_server->conf, "DefaultChdir", FALSE);
436
0
        c = add_config_param_set(&main_server->conf, "DefaultChdir", 1, NULL);
437
0
        c->argv[0] = pstrdup(c->pool, default_chdir);
438
439
0
        pr_log_debug(DEBUG9, "AuthUnixOption magicTokenChroot: "
440
0
          "found magic token in '%s', using 'DefaultRoot %s' and "
441
0
          "'DefaultChdir %s'", pw->pw_dir, home_dir, default_chdir);
442
443
        /* We need to use a long-lived memory pool for overwriting the
444
         * normal home directory.
445
         */
446
0
        pw->pw_dir = pstrdup(session.pool, home_dir);
447
448
0
        break;
449
0
      }
450
451
0
      ptr = strchr(ptr + 2, '.');
452
0
    }
453
0
  }
454
455
0
  return pw ? mod_create_data(cmd, pw) : PR_DECLINED(cmd);
456
0
}
457
458
0
MODRET pw_getgrnam(cmd_rec *cmd) {
459
0
  struct group *gr = NULL;
460
0
  const char *name;
461
462
0
  name = cmd->argv[0];
463
0
  if (unix_persistent_passwd) {
464
0
    gr = p_getgrnam(name);
465
466
0
  } else {
467
0
    gr = getgrnam(name);
468
0
  }
469
470
0
  return gr ? mod_create_data(cmd, gr) : PR_DECLINED(cmd);
471
0
}
472
473
0
MODRET pw_getgrgid(cmd_rec *cmd) {
474
0
  struct group *gr = NULL;
475
0
  gid_t gid;
476
477
0
  gid = *((gid_t *) cmd->argv[0]);
478
0
  if (unix_persistent_passwd) {
479
0
    gr = p_getgrgid(gid);
480
481
0
  } else {
482
0
    gr = getgrgid(gid);
483
0
  }
484
485
0
  return gr ? mod_create_data(cmd, gr) : PR_DECLINED(cmd);
486
0
}
487
488
#ifdef PR_USE_SHADOW
489
static char *get_pwd_info(pool *p, const char *u, time_t *lstchg, time_t *min,
490
0
    time_t *max, time_t *warn, time_t *inact, time_t *expire) {
491
0
  struct spwd *sp;
492
0
  char *cpw = NULL;
493
494
0
  pr_trace_msg(trace_channel, 7,
495
0
    "looking up user '%s' via Unix shadow mechanism", u);
496
497
0
  PRIVS_ROOT
498
0
#ifdef HAVE_SETSPENT
499
0
  setspent();
500
0
#endif /* HAVE_SETSPENT */
501
502
0
  sp = getspnam(u);
503
0
  if (sp != NULL) {
504
0
    cpw = pstrdup(p, sp->sp_pwdp);
505
506
0
    if (lstchg != NULL) {
507
0
      *lstchg = SP_CVT_DAYS(sp->sp_lstchg);
508
0
    }
509
510
0
    if (min != NULL) {
511
0
      *min = SP_CVT_DAYS(sp->sp_min);
512
0
    }
513
514
0
    if (max != NULL) {
515
0
      *max = SP_CVT_DAYS(sp->sp_max);
516
0
    }
517
518
0
#ifdef HAVE_SPWD_SP_WARN
519
0
    if (warn != NULL) {
520
0
      *warn = SP_CVT_DAYS(sp->sp_warn);
521
0
    }
522
0
#endif /* HAVE_SPWD_SP_WARN */
523
524
0
#ifdef HAVE_SPWD_SP_INACT
525
0
    if (inact != NULL) {
526
0
      *inact = SP_CVT_DAYS(sp->sp_inact);
527
0
    }
528
0
#endif /* HAVE_SPWD_SP_INACT */
529
530
0
#ifdef HAVE_SPWD_SP_EXPIRE
531
0
    if (expire != NULL) {
532
0
      *expire = SP_CVT_DAYS(sp->sp_expire);
533
0
    }
534
0
#endif /* HAVE_SPWD_SP_EXPIRE */
535
536
0
  } else {
537
0
    pr_log_debug(DEBUG5, "mod_auth_unix: getspnam(3) for user '%s' error: %s",
538
0
      u, strerror(errno));
539
0
  }
540
541
#ifdef PR_USE_AUTO_SHADOW
542
  if (sp == NULL) {
543
    struct passwd *pw;
544
545
    pr_trace_msg(trace_channel, 7,
546
      "looking up user '%s' via Unix autoshadow mechanism", u);
547
548
    endspent();
549
    PRIVS_RELINQUISH
550
551
    pw = getpwnam(u);
552
    if (pw != NULL) {
553
      cpw = pstrdup(p, pw->pw_passwd);
554
555
      if (lstchg != NULL) {
556
        *lstchg = (time_t) -1;
557
      }
558
559
      if (min != NULL) {
560
        *min = (time_t) -1;
561
      }
562
563
      if (max != NULL) {
564
        *max = (time_t) -1;
565
      }
566
567
      if (warn != NULL) {
568
        *warn = (time_t) -1;
569
      }
570
571
      if (inact != NULL) {
572
        *inact = (time_t) -1;
573
      }
574
575
      if (expire != NULL) {
576
        *expire = (time_t) -1;
577
      }
578
579
    } else {
580
      pr_log_debug(DEBUG5, "mod_auth_unix: getpwnam(3) for user '%s' error: %s",
581
        u, strerror(errno));
582
    }
583
584
  } else {
585
    PRIVS_RELINQUISH
586
  }
587
#else
588
0
  endspent();
589
0
  PRIVS_RELINQUISH
590
0
#endif /* PR_USE_AUTO_SHADOW */
591
592
0
  return cpw;
593
0
}
594
595
#else /* PR_USE_SHADOW */
596
597
static char *get_pwd_info(pool *p, const char *u, time_t *lstchg, time_t *min,
598
    time_t *max, time_t *warn, time_t *inact, time_t *expire) {
599
  char *cpw = NULL;
600
#if defined(HAVE_GETPRPWENT) || defined(COMSEC)
601
  struct pr_passwd *prpw;
602
#endif
603
#if !defined(HAVE_GETPRPWENT) || defined(COMSEC)
604
  struct passwd *pw;
605
#endif
606
607
 /* Some platforms (i.e. BSD) provide "transparent" shadowing, which
608
  * requires that we are root in order to have the password member
609
  * filled in.
610
  */
611
612
  pr_trace_msg(trace_channel, 7,
613
    "looking up user '%s' via normal Unix mechanism", u);
614
615
  PRIVS_ROOT
616
#if !defined(HAVE_GETPRPWENT) || defined(COMSEC)
617
# ifdef COMSEC
618
  if (!iscomsec()) {
619
# endif /* COMSEC */
620
  endpwent();
621
#if defined(BSDI3) || defined(BSDI4)
622
  /* endpwent() seems to be buggy on BSDI3.1 (is this true for 4.0?)
623
   * setpassent(0) _seems_ to do the same thing, however this conflicts
624
   * with the man page documented behavior.  Argh, why do all the bsds
625
   * have to be different in this area (except OpenBSD, grin).
626
   */
627
  setpassent(0);
628
#else /* BSDI3 || BSDI4 */
629
  setpwent();
630
#endif /* BSDI3 || BSDI4 */
631
632
  pw = getpwnam(u);
633
  if (pw) {
634
    cpw = pstrdup(p, pw->pw_passwd);
635
636
    if (lstchg)
637
      *lstchg = (time_t) -1;
638
639
    if (min)
640
      *min = (time_t) -1;
641
642
    if (max)
643
      *max = (time_t) -1;
644
645
    if (warn)
646
      *warn = (time_t) -1;
647
648
    if (inact)
649
      *inact = (time_t) -1;
650
651
    if (expire)
652
      *expire = (time_t) -1;
653
654
  } else {
655
    pr_log_debug(DEBUG5, "mod_auth_unix: getpwnam(3) for user '%s' error: %s",
656
      u, strerror(errno));
657
  }
658
659
  endpwent();
660
#ifdef COMSEC
661
  } else {
662
#endif /* COMSEC */
663
#endif /* !HAVE_GETPRWENT or COMSEC */
664
665
#if defined(HAVE_GETPRPWENT) || defined(COMSEC)
666
  endprpwent();
667
  setprpwent();
668
669
  prpw = getprpwnam((char *) u);
670
671
  if (prpw) {
672
    cpw = pstrdup(p, prpw->ufld.fd_encrypt);
673
674
    if (lstchg)
675
      *lstchg = (time_t) -1;
676
677
    if (min)
678
      *min = prpw->ufld.fd_min;
679
680
    if (max)
681
      *max = (time_t) -1;
682
683
    if (warn)
684
      *warn = (time_t) -1;
685
686
    if (inact)
687
      *inact = (time_t) -1;
688
689
    if (expire)
690
      *expire = prpw->ufld.fd_expire;
691
  }
692
693
  endprpwent();
694
#ifdef COMSEC
695
  }
696
#endif /* COMSEC */
697
#endif /* HAVE_GETPRPWENT or COMSEC */
698
699
  PRIVS_RELINQUISH
700
#if defined(BSDI3) || defined(BSDI4)
701
  setpassent(1);
702
#endif
703
  return cpw;
704
}
705
706
#endif /* PR_USE_SHADOW */
707
708
/* High-level auth handlers
709
 */
710
711
/* cmd->argv[0] : user name
712
 * cmd->argv[1] : cleartext password
713
 */
714
715
0
MODRET pw_auth(cmd_rec *cmd) {
716
0
  int res;
717
0
  time_t now;
718
0
  char *cleartxt_passwd;
719
0
  time_t lstchg = -1, max = -1, inact = -1, expire = -1;
720
0
  const char *name;
721
0
  size_t cleartxt_passwdlen;
722
723
0
  name = cmd->argv[0];
724
725
0
  cleartxt_passwd = get_pwd_info(cmd->tmp_pool, name, &lstchg, NULL, &max,
726
0
    NULL, &inact, &expire);
727
0
  if (cleartxt_passwd == NULL) {
728
0
    return PR_DECLINED(cmd);
729
0
  }
730
731
0
  res = pr_auth_check(cmd->tmp_pool, cleartxt_passwd, cmd->argv[0],
732
0
    cmd->argv[1]);
733
0
  cleartxt_passwdlen = strlen(cleartxt_passwd);
734
0
  pr_memscrub(cleartxt_passwd, cleartxt_passwdlen);
735
736
0
  if (res < PR_AUTH_OK) {
737
0
    return PR_ERROR_INT(cmd, res);
738
0
  }
739
740
0
  time(&now);
741
742
0
  if (lstchg > (time_t) 0 &&
743
0
      max > (time_t) 0 &&
744
0
      inact > (time_t) 0) {
745
0
    if (now > (lstchg + max + inact)) {
746
0
      pr_trace_msg("auth", 7, "password last changed for user '%s' on %s + "
747
0
        "maximum time between password changes (%lu secs) + "
748
0
        "inactivity time after expiration (%lu secs) = %s, "
749
0
        "rejecting authorization with AGEPWD", name, pr_strtime(lstchg),
750
0
        (unsigned long) max, (unsigned long) inact,
751
0
        pr_strtime(lstchg + max + inact));
752
0
      return PR_ERROR_INT(cmd, PR_AUTH_AGEPWD);
753
0
    }
754
0
  }
755
756
0
  if (expire > (time_t) 0 &&
757
0
      now > expire) {
758
0
    pr_trace_msg("auth", 7, "password expired for user '%s' expired on %s, "
759
0
      "rejecting authorization with DISABLEDPWD", name, pr_strtime(now));
760
0
    return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD);
761
0
  }
762
763
0
  session.auth_mech = "mod_auth_unix.c";
764
0
  return PR_HANDLED(cmd);
765
0
}
766
767
0
MODRET pw_authz(cmd_rec *cmd) {
768
0
  time_t now;
769
0
  char *user, *cleartxt_passwd;
770
0
  time_t lstchg = -1, max = -1, inact = -1, expire = -1;
771
0
  size_t cleartxt_passwdlen;
772
773
0
  user = cmd->argv[0];
774
775
0
  cleartxt_passwd = get_pwd_info(cmd->tmp_pool, user, &lstchg, NULL, &max,
776
0
    NULL, &inact, &expire);
777
0
  if (cleartxt_passwd == NULL) {
778
0
    pr_trace_msg(trace_channel, 3,
779
0
      "no password information found for user '%.100s'", user);
780
0
    return PR_DECLINED(cmd);
781
0
  }
782
783
0
  cleartxt_passwdlen = strlen(cleartxt_passwd);
784
0
  pr_memscrub(cleartxt_passwd, cleartxt_passwdlen);
785
786
0
  time(&now);
787
788
0
  if (lstchg > (time_t) 0 &&
789
0
      max > (time_t) 0 &&
790
0
      inact > (time_t) 0) {
791
0
    if (now > (lstchg + max + inact)) {
792
0
      pr_log_auth(LOG_WARNING,
793
0
        "account for user '%.100s' disabled due to inactivity", user);
794
0
      return PR_ERROR_INT(cmd, PR_AUTH_AGEPWD);
795
0
    }
796
0
  }
797
798
0
  if (expire > (time_t) 0 &&
799
0
      now > expire) {
800
0
    pr_log_auth(LOG_WARNING,
801
0
      "account for user '%.100s' disabled due to password expiration", user);
802
0
    return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD);
803
0
  }
804
805
  /* XXX Any other implementations here? */
806
807
#ifdef HAVE_LOGINRESTRICTIONS
808
  if (!(auth_unix_opts & AUTH_UNIX_OPT_AIX_NO_RLOGIN)) {
809
    int res, xerrno, code = 0;
810
    char *reason = NULL;
811
812
    /* Check for account login restrictions and such using AIX-specific
813
     * functions.
814
     */
815
    PRIVS_ROOT
816
    res = loginrestrictions(user, S_RLOGIN, NULL, &reason);
817
    xerrno = errno;
818
    PRIVS_RELINQUISH
819
820
    if (res != 0) {
821
      if (reason != NULL &&
822
          *reason) {
823
        pr_trace_msg(trace_channel, 9,
824
          "AIX loginrestrictions() failed for user '%s': %.100s", user, reason);
825
        pr_log_auth(LOG_WARNING, "login restricted for user '%s': %.100s",
826
          user, reason);
827
      }
828
829
      pr_log_auth(LOG_NOTICE,
830
        "AIX loginrestrictions() failed for user '%s': %s", user,
831
        strerror(xerrno));
832
833
      return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD);
834
    }
835
836
    PRIVS_ROOT
837
    code = passwdexpired(user, &reason);
838
    PRIVS_RELINQUISH
839
840
    switch (code) {
841
      case 0:
842
        /* Password not expired for user */
843
        break;
844
845
      case 1:
846
        /* Password expired and needs to be changed */
847
        pr_log_auth(LOG_WARNING, "password expired for user '%s': %.100s",
848
          cmd->argv[0], reason);
849
        return PR_ERROR_INT(cmd, PR_AUTH_AGEPWD);
850
851
      case 2:
852
        /* Password expired, requires sysadmin to change it */
853
        pr_log_auth(LOG_WARNING,
854
          "password expired for user '%s', requires sysadmin intervention: "
855
          "%.100s", user, reason);
856
        return PR_ERROR_INT(cmd, PR_AUTH_AGEPWD);
857
858
      default:
859
        /* Other error */
860
        pr_log_auth(LOG_WARNING, "AIX passwdexpired() failed for user '%s': "
861
          "%.100s", user, reason);
862
        return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD);
863
    }
864
  }
865
#endif /* !HAVE_LOGINRESTRICTIONS */
866
867
0
  return PR_HANDLED(cmd);
868
0
}
869
870
/* cmd->argv[0] = hashed password
871
 * cmd->argv[1] = user
872
 * cmd->argv[2] = cleartext
873
 */
874
875
0
MODRET pw_check(cmd_rec *cmd) {
876
0
  const char *cpw = cmd->argv[0];
877
0
  const char *pw = cmd->argv[2];
878
0
  modret_t *mr = NULL;
879
0
  cmd_rec *cmd2 = NULL;
880
0
  char *crypted_text = NULL;
881
882
#ifdef PR_USE_SIA
883
  SIAENTITY *ent = NULL;
884
  int res = SIASUCCESS;
885
  char *info[2];
886
  struct passwd *pwd;
887
  char *user = NULL;
888
#endif
889
890
#ifdef COMSEC
891
  if (iscomsec()) {
892
    if (strcmp(bigcrypt((char *) pw, (char *) cpw), cpw) != 0) {
893
      return PR_DECLINED(cmd);
894
    }
895
896
  } else {
897
#endif /* COMSEC */
898
899
#ifdef PR_USE_SIA
900
  /* Use Tru64's C2 SIA subsystem for authenticating this user. */
901
  user = cmd->argv[1];
902
903
  pr_log_auth(PR_LOG_INFO, "using SIA for user '%s'", user);
904
905
  info[0] = "ProFTPD";
906
  info[1] = NULL;
907
908
  /* Prepare the SIA subsystem. */
909
  PRIVS_ROOT
910
  res = sia_ses_init(&ent, 1, info, NULL, user, NULL, 0, NULL);
911
  if (res != SIASUCCESS) {
912
    pr_log_auth(PR_LOG_NOTICE, "sia_ses_init() returned %d for user '%s'", res,
913
      user);
914
915
  } else {
916
917
    res = sia_ses_authent(NULL, pw, ent);
918
    if (res != SIASUCCESS) {
919
      sia_ses_release(&ent);
920
      PRIVS_RELINQUISH
921
      pr_log_auth(PR_LOG_NOTICE, "sia_ses_authent() returned %d for user '%s'",
922
        res, user);
923
      return PR_ERROR(cmd);
924
    }
925
926
    res = sia_ses_estab(NULL, ent);
927
    if (res != SIASUCCESS) {
928
      PRIVS_RELINQUISH
929
      pr_log_auth(PR_LOG_NOTICE, "sia_ses_estab() returned %d for user '%s'",
930
        res, user);
931
      return PR_ERROR(cmd);
932
    }
933
934
    res = sia_ses_release(&ent);
935
    if (res != SIASUCCESS) {
936
      PRIVS_RELINQUISH
937
      pr_log_auth(PR_LOG_NOTICE, "sia_ses_release() returned %d", res);
938
      return PR_ERROR(cmd);
939
    }
940
  }
941
  PRIVS_RELINQUISH
942
943
  if (res != SIASUCCESS) {
944
    return PR_DECLINED(cmd);
945
  }
946
947
#else /* !PR_USE_SIA */
948
949
# ifdef CYGWIN
950
  /* We have to do special Windows NT voodoo with Cygwin in order to be
951
   * able to switch UID/GID. More info at
952
   * http://cygwin.com/cygwin-ug-net/ntsec.html#NTSEC-SETUID
953
   */
954
  if (GetVersion() < 0x80000000) {
955
    struct passwd *pwent = NULL;
956
    HANDLE token;
957
958
    /* A struct passwd * is needed.  To look one up via pw_getpwnam(), though,
959
     * we'll need a cmd_rec.
960
     */
961
    cmd2 = pr_cmd_alloc(cmd->tmp_pool, 1, cmd->argv[1]);
962
963
    /* pw_getpwnam() returns a MODRET, so we need to handle that.  Yes, this
964
     * might have been easier if we'd used pr_auth_getpwnam(), but that would
965
     * dispatch through other auth modules, which is _not_ what we want.
966
     */
967
    mr = pw_getpwnam(cmd2);
968
969
    /* Note: we don't handle the case where pw_getpwnam() returns anything
970
     * other than HANDLED at the moment.
971
     */
972
973
    if (MODRET_ISHANDLED(mr) &&
974
        MODRET_HASDATA(mr)) {
975
      pwent = mr->data;
976
977
      token = cygwin_logon_user((const struct passwd *) pwent, pw);
978
      if (token == INVALID_HANDLE_VALUE) {
979
        pr_log_pri(PR_LOG_NOTICE, "error authenticating Cygwin user: %s",
980
          strerror(errno));
981
        return PR_DECLINED(cmd);
982
      }
983
984
      cygwin_set_impersonation_token(token);
985
986
    } else {
987
      return PR_DECLINED(cmd);
988
    }
989
990
  } else {
991
# endif /* CYGWIN */
992
993
#ifdef HAVE_AUTHENTICATE
994
  if (!(auth_unix_opts & AUTH_UNIX_OPT_AIX_NO_AUTHENTICATE)) {
995
    int res, xerrno, reenter = 0;
996
    char *user, *passwd, *msg = NULL;
997
998
    user = cmd->argv[1];
999
    passwd = cmd->argv[2];
1000
1001
    pr_trace_msg(trace_channel, 9, "calling AIX authenticate() for user '%s'",
1002
      user);
1003
1004
    PRIVS_ROOT
1005
    do {
1006
      res = authenticate(user, passwd, &reenter, &msg);
1007
      xerrno = errno;
1008
1009
      pr_trace_msg(trace_channel, 9,
1010
        "AIX authenticate result: %d (msg '%.100s')", res, msg);
1011
1012
    } while (reenter != 0);
1013
# if defined(HAVE_LOGINSUCCESS)
1014
    if (res == 0) {
1015
      const char *host, *sess_ttyname;
1016
      char *msg = NULL;
1017
1018
      host = pr_netaddr_get_dnsstr(session.c->remote_addr);
1019
      sess_ttyname = pr_session_get_ttyname(cmd->tmp_pool);
1020
1021
      if (loginsuccess(user, (char *) host, (char *) sess_ttyname, &msg) == 0) {
1022
        if (msg != NULL) {
1023
          pr_trace_msg("auth", 14, "AIX loginsuccess() report: %s", msg);
1024
        }
1025
1026
      } else {
1027
        pr_trace_msg("auth", 3, "AIX loginsuccess() error for user '%s', "
1028
          "host '%s', tty '%s': %s", user, host, sess_ttyname, strerror(errno));
1029
      }
1030
    }
1031
# endif /* HAVE_LOGINSUCCESS */
1032
    PRIVS_RELINQUISH
1033
1034
    /* AIX indicates failure with a return value of 1. */
1035
    if (res != 0) {
1036
      pr_log_auth(LOG_WARNING,
1037
       "AIX authenticate failed for user '%s': %.100s", user, msg);
1038
1039
      if (xerrno == ENOENT) {
1040
        return PR_ERROR_INT(cmd, PR_AUTH_NOPWD);
1041
      }
1042
1043
      return PR_ERROR_INT(cmd, PR_AUTH_DISABLEDPWD);
1044
    }
1045
  }
1046
#endif /* HAVE_AUTHENTICATE */
1047
1048
  /* Call pw_authz here, to make sure the user is authorized to login. */
1049
1050
0
  if (cmd2 == NULL) {
1051
0
    cmd2 = pr_cmd_alloc(cmd->tmp_pool, 1, cmd->argv[1]);
1052
0
  }
1053
1054
0
  mr = pw_authz(cmd2);
1055
0
  if (MODRET_ISERROR(mr)) {
1056
0
    int err_code;
1057
1058
0
    err_code = MODRET_ERROR(mr);
1059
0
    return PR_ERROR_INT(cmd, err_code);
1060
0
  }
1061
1062
0
  if (MODRET_ISDECLINED(mr)) {
1063
0
    return PR_DECLINED(cmd);
1064
0
  }
1065
1066
0
  crypted_text = (char *) crypt(pw, cpw);
1067
0
  if (crypted_text == NULL) {
1068
0
    pr_log_pri(PR_LOG_NOTICE, "crypt(3) failed: %s", strerror(errno));
1069
0
    return PR_DECLINED(cmd);
1070
0
  }
1071
1072
0
  if (strcmp(crypted_text, cpw) != 0) {
1073
0
    return PR_DECLINED(cmd);
1074
0
  }
1075
1076
# ifdef CYGWIN
1077
  }
1078
# endif /* CYGWIN */
1079
1080
0
#endif /* PR_USE_SIA */
1081
1082
#ifdef COMSEC
1083
  }
1084
#endif /* COMSEC */
1085
1086
0
  session.auth_mech = "mod_auth_unix.c";
1087
0
  return PR_HANDLED(cmd);
1088
0
}
1089
1090
0
MODRET pw_uid2name(cmd_rec *cmd) {
1091
0
  struct passwd *pw = NULL;
1092
0
  uid_t uid;
1093
1094
0
  uid = *((uid_t *) cmd->argv[0]);
1095
1096
0
  if (unix_persistent_passwd) {
1097
0
    pw = p_getpwuid(uid);
1098
1099
0
  } else {
1100
0
    pw = getpwuid(uid);
1101
0
  }
1102
1103
0
  if (pw) {
1104
0
    return mod_create_data(cmd, pw->pw_name);
1105
0
  }
1106
1107
0
  return PR_DECLINED(cmd);
1108
0
}
1109
1110
0
MODRET pw_gid2name(cmd_rec *cmd) {
1111
0
  struct group *gr = NULL;
1112
0
  gid_t gid;
1113
1114
0
  gid = *((gid_t *) cmd->argv[0]);
1115
0
  if (unix_persistent_passwd) {
1116
0
    gr = p_getgrgid(gid);
1117
1118
0
  } else {
1119
0
    gr = getgrgid(gid);
1120
0
  }
1121
1122
0
  if (gr) {
1123
0
    return mod_create_data(cmd, gr->gr_name);
1124
0
  }
1125
1126
0
  return PR_DECLINED(cmd);
1127
0
}
1128
1129
0
MODRET pw_name2uid(cmd_rec *cmd) {
1130
0
  struct passwd *pw = NULL;
1131
0
  const char *name;
1132
1133
0
  name = cmd->argv[0];
1134
1135
0
  if (unix_persistent_passwd) {
1136
0
    pw = p_getpwnam(name);
1137
1138
0
  } else {
1139
0
    pw = getpwnam(name);
1140
0
  }
1141
1142
0
  return pw ? mod_create_data(cmd, (void *) &pw->pw_uid) : PR_DECLINED(cmd);
1143
0
}
1144
1145
0
MODRET pw_name2gid(cmd_rec *cmd) {
1146
0
  struct group *gr = NULL;
1147
0
  const char *name;
1148
1149
0
  name = cmd->argv[0];
1150
1151
0
  if (unix_persistent_passwd) {
1152
0
    gr = p_getgrnam(name);
1153
1154
0
  } else {
1155
0
    gr = getgrnam(name);
1156
0
  }
1157
1158
0
  return gr ? mod_create_data(cmd, (void *) &gr->gr_gid) : PR_DECLINED(cmd);
1159
0
}
1160
1161
static int get_groups_by_getgrset(const char *user, gid_t primary_gid,
1162
    array_header *gids, array_header *groups,
1163
0
    struct group *(*my_getgrgid)(gid_t)) {
1164
0
  int res;
1165
#ifdef HAVE_GETGRSET
1166
  gid_t group_ids[NGROUPS_MAX];
1167
  unsigned int ngroups = 0;
1168
  register unsigned int i;
1169
  char *grgid, *grouplist, *ptr;
1170
1171
  pr_trace_msg("auth", 4,
1172
    "using getgrset(3) to look up group membership");
1173
1174
  grouplist = getgrset(user);
1175
  if (grouplist == NULL) {
1176
    int xerrno = errno;
1177
1178
    pr_log_pri(PR_LOG_WARNING, "getgrset(3) error: %s", strerror(xerrno));
1179
1180
    errno = xerrno;
1181
    return -1;
1182
  }
1183
1184
  ptr = grouplist;
1185
  memset(group_ids, 0, sizeof(group_ids));
1186
1187
  /* The getgrset(3) function returns a string which is a comma-delimited
1188
   * list of group IDs.
1189
   */
1190
  grgid = strsep(&grouplist, ",");
1191
  while (grgid != NULL) {
1192
    gid_t gid;
1193
1194
    pr_signals_handle();
1195
1196
    if (ngroups >= sizeof(group_ids)) {
1197
      /* Reached capacity of the group_ids array. */
1198
      break;
1199
    }
1200
1201
    pr_str2gid(grgid, &gid);
1202
1203
    /* Skip the primary group. */
1204
    if (gid == primary_gid) {
1205
      grgid = strsep(&grouplist, ",");
1206
      continue;
1207
    }
1208
1209
    group_ids[ngroups] = gid;
1210
    ngroups++;
1211
1212
    grgid = strsep(&grouplist, ",");
1213
  }
1214
1215
  for (i = 0; i < ngroups; i++) {
1216
    struct group *gr;
1217
1218
    gr = my_getgrgid(group_ids[i]);
1219
    if (gr != NULL) {
1220
      if (gids != NULL &&
1221
          primary_gid != gr->gr_gid) {
1222
        *((gid_t *) push_array(gids)) = gr->gr_gid;
1223
      }
1224
1225
      if (groups != NULL &&
1226
          primary_gid != gr->gr_gid) {
1227
        *((char **) push_array(groups)) = pstrdup(session.pool,
1228
          gr->gr_name);
1229
      }
1230
    }
1231
  }
1232
1233
  free(ptr);
1234
  res = 0;
1235
1236
#else
1237
0
  errno = ENOSYS;
1238
0
  res = -1;
1239
0
#endif /* HAVE_GETGRSET */
1240
1241
0
  return res;
1242
0
}
1243
1244
static int get_groups_by_getgrouplist(const char *user, gid_t primary_gid,
1245
    array_header *gids, array_header *groups,
1246
0
    struct group *(*my_getgrgid)(gid_t)) {
1247
0
  int res;
1248
0
#ifdef HAVE_GETGROUPLIST
1249
0
  int use_getgrouplist = TRUE;
1250
0
  gid_t group_ids[NGROUPS_MAX];
1251
0
  int ngroups = NGROUPS_MAX;
1252
0
  register int i;
1253
1254
  /* Determine whether to use getgrouplist(3), if available.  Older glibc
1255
   * versions (i.e. 2.2.4 and older) had buggy getgrouplist() implementations
1256
   * which allowed for buffer overflows (see CVS-2003-0689); do not use
1257
   * getgrouplist() on such glibc versions.
1258
   */
1259
1260
# if defined(__GLIBC__) && \
1261
     defined(__GLIBC_MINOR__) && \
1262
     __GLIBC__ <= 2 && \
1263
     __GLIBC_MINOR__ < 3
1264
  use_getgrouplist = FALSE;
1265
# endif
1266
1267
  /* Use of getgrouplist(3) might have been disabled via the "NoGetgrouplist"
1268
   * AuthUnixOption as well.
1269
   */
1270
0
  if (auth_unix_opts & AUTH_UNIX_OPT_NO_GETGROUPLIST) {
1271
0
    use_getgrouplist = FALSE;
1272
0
  }
1273
1274
0
  if (use_getgrouplist == FALSE) {
1275
0
    errno = ENOSYS;
1276
0
    return -1;
1277
0
  }
1278
1279
0
  pr_trace_msg("auth", 4,
1280
0
    "using getgrouplist(3) to look up group membership");
1281
1282
0
  memset(group_ids, 0, sizeof(group_ids));
1283
#ifdef HAVE_GETGROUPLIST_TAKES_INTS
1284
  res = getgrouplist(user, primary_gid, (int *) group_ids, &ngroups);
1285
#else
1286
0
  res = getgrouplist(user, primary_gid, group_ids, &ngroups);
1287
0
#endif
1288
0
  if (res < 0) {
1289
0
    int xerrno = errno;
1290
1291
0
    pr_log_pri(PR_LOG_WARNING, "getgrouplist(3) error: %s", strerror(xerrno));
1292
1293
0
    errno = xerrno;
1294
0
    return -1;
1295
0
  }
1296
1297
0
  for (i = 0; i < ngroups; i++) {
1298
0
    struct group *gr;
1299
1300
0
    gr = my_getgrgid(group_ids[i]);
1301
0
    if (gr != NULL) {
1302
0
      if (gids != NULL &&
1303
0
          primary_gid != gr->gr_gid) {
1304
0
        *((gid_t *) push_array(gids)) = gr->gr_gid;
1305
0
      }
1306
1307
0
      if (groups != NULL &&
1308
0
          primary_gid != gr->gr_gid) {
1309
0
        *((char **) push_array(groups)) = pstrdup(session.pool,
1310
0
          gr->gr_name);
1311
0
      }
1312
0
    }
1313
0
  }
1314
1315
0
  res = 0;
1316
#else
1317
  errno = ENOSYS;
1318
  res = -1;
1319
#endif /* HAVE_GETGROUPLIST */
1320
1321
0
  return res;
1322
0
}
1323
1324
static int get_groups_by_getgrent(const char *user, gid_t primary_gid,
1325
    array_header *gids, array_header *groups,
1326
0
    struct group *(*my_getgrent)(void)) {
1327
0
  struct group *gr;
1328
0
  size_t user_len;
1329
1330
  /* This is where things get slow, expensive, and ugly.  Loop through
1331
   * everything, checking to make sure we haven't already added it.
1332
   */
1333
0
  user_len = strlen(user);
1334
0
  while ((gr = my_getgrent()) != NULL &&
1335
0
         gr->gr_mem != NULL) {
1336
0
    char **gr_member = NULL;
1337
1338
0
    pr_signals_handle();
1339
1340
    /* Loop through each member name listed */
1341
0
    for (gr_member = gr->gr_mem; *gr_member; gr_member++) {
1342
1343
     /* If it matches the given username... */
1344
0
      if (strncmp(*gr_member, user, user_len + 1) == 0) {
1345
1346
0
        if (gids != NULL &&
1347
0
            primary_gid != gr->gr_gid) {
1348
0
          *((gid_t *) push_array(gids)) = gr->gr_gid;
1349
0
        }
1350
1351
0
        if (groups != NULL &&
1352
0
            primary_gid != gr->gr_gid) {
1353
0
          *((char **) push_array(groups)) = pstrdup(session.pool,
1354
0
            gr->gr_name);
1355
0
        }
1356
0
      }
1357
0
    }
1358
0
  }
1359
1360
0
  return 0;
1361
0
}
1362
1363
static int get_groups_by_initgroups(const char *user, gid_t primary_gid,
1364
    array_header *gids, array_header *groups,
1365
0
    struct group *(*my_getgrgid)(gid_t)) {
1366
0
  int res;
1367
0
#if defined(HAVE_INITGROUPS) && defined(HAVE_GETGROUPS)
1368
0
  gid_t group_ids[NGROUPS_MAX+1];
1369
0
  int ngroups, use_initgroups = TRUE, xerrno;
1370
0
  register int i;
1371
1372
  /* On Mac OSX, the getgroups(2) man page has this unsettling tidbit:
1373
   *
1374
   *  Calling initgroups(3) to opt-in for supplementary groups will cause
1375
   *  getgroups() to return a single entry, the GID that was passed to
1376
   *  initgroups(3).
1377
   *
1378
   * But in our case, we WANT all of those groups.  Thus on Mac OSX, we
1379
   * will skip the use of initgroups(3) in favor of other mechanisms
1380
   * (e.g. getgrouplist(3)).
1381
   */
1382
# if defined(DARWIN10) || \
1383
     defined(DARWIN11) || \
1384
     defined(DARWIN12) || \
1385
     defined(DARWIN13) || \
1386
     defined(DARWIN14) || \
1387
     defined(DARWIN15)
1388
  use_initgroups = FALSE;
1389
# endif /* Mac OSX */
1390
1391
  /* Use of initgroups(3) might have been disabled via the "NoInitgroups"
1392
   * AuthUnixOption as well.
1393
   */
1394
0
  if (auth_unix_opts & AUTH_UNIX_OPT_NO_INITGROUPS) {
1395
0
    use_initgroups = FALSE;
1396
0
  }
1397
1398
  /* If we are not root, then initgroups(3) will most likely fail. */
1399
0
  if (geteuid() != PR_ROOT_UID) {
1400
0
    use_initgroups = FALSE;
1401
0
  }
1402
1403
0
  if (use_initgroups == FALSE) {
1404
0
    errno = ENOSYS;
1405
0
    return -1;
1406
0
  }
1407
1408
0
  pr_trace_msg("auth", 4,
1409
0
    "using initgroups(3) to look up group membership");
1410
1411
0
  PRIVS_ROOT
1412
0
  res = initgroups(user, primary_gid);
1413
0
  xerrno = errno;
1414
0
  PRIVS_RELINQUISH
1415
1416
0
  if (res < 0) {
1417
0
    pr_log_pri(PR_LOG_WARNING, "initgroups(3) error: %s", strerror(xerrno));
1418
1419
0
    errno = xerrno;
1420
0
    return -1;
1421
0
  }
1422
1423
0
  ngroups = getgroups(NGROUPS_MAX+1, group_ids);
1424
0
  if (ngroups < 0) {
1425
0
    xerrno = errno;
1426
1427
0
    pr_log_pri(PR_LOG_WARNING, "getgroups(2) error: %s", strerror(xerrno));
1428
1429
0
    errno = xerrno;
1430
0
    return -1;
1431
0
  }
1432
1433
0
  for (i = 0; i < ngroups; i++) {
1434
0
    struct group *gr;
1435
1436
0
    gr = my_getgrgid(group_ids[i]);
1437
0
    if (gr != NULL) {
1438
0
      if (gids != NULL &&
1439
0
          primary_gid != gr->gr_gid) {
1440
0
        *((gid_t *) push_array(gids)) = gr->gr_gid;
1441
0
      }
1442
1443
0
      if (groups != NULL &&
1444
0
          primary_gid != gr->gr_gid) {
1445
0
        *((char **) push_array(groups)) = pstrdup(session.pool,
1446
0
          gr->gr_name);
1447
0
      }
1448
0
    }
1449
0
  }
1450
1451
0
  res = 0;
1452
1453
#else
1454
  errno = ENOSYS;
1455
  res = -1;
1456
#endif /* HAVE_INITGROUPS and HAVE_GETGROUPS */
1457
1458
0
  return res;
1459
0
}
1460
1461
/* cmd->argv[0] = name
1462
 * cmd->argv[1] = (array_header **) group_ids
1463
 * cmd->argv[2] = (array_header **) group_names
1464
 */
1465
0
MODRET pw_getgroups(cmd_rec *cmd) {
1466
0
  int res;
1467
0
  struct passwd *pw = NULL;
1468
0
  struct group *gr = NULL;
1469
0
  array_header *gids = NULL, *groups = NULL;
1470
0
  const char *name = NULL;
1471
1472
  /* Function pointers for which lookup functions to use */
1473
0
  struct passwd *(*my_getpwnam)(const char *) = NULL;
1474
0
  struct group *(*my_getgrgid)(gid_t) = NULL;
1475
0
  struct group *(*my_getgrent)(void) = NULL;
1476
0
  RETSETGRENTTYPE (*my_setgrent)(void) = NULL;
1477
1478
  /* Play function pointer games */
1479
0
  if (unix_persistent_passwd) {
1480
0
    my_getpwnam = p_getpwnam;
1481
0
    my_getgrgid = p_getgrgid;
1482
0
    my_getgrent = p_getgrent;
1483
0
    my_setgrent = p_setgrent;
1484
1485
0
  } else {
1486
0
    my_getpwnam = getpwnam;
1487
0
    my_getgrgid = getgrgid;
1488
0
    my_getgrent = getgrent;
1489
0
    my_setgrent = setgrent;
1490
0
  }
1491
1492
0
  name = cmd->argv[0];
1493
1494
0
  if (cmd->argv[1] != NULL) {
1495
0
    gids = (array_header *) cmd->argv[1];
1496
0
  }
1497
1498
0
  if (cmd->argv[2] != NULL) {
1499
0
    groups = (array_header *) cmd->argv[2];
1500
0
  }
1501
1502
  /* Retrieve the necessary info. */
1503
0
  if (name == NULL ||
1504
0
      !(pw = my_getpwnam(name))) {
1505
0
    return PR_DECLINED(cmd);
1506
0
  }
1507
1508
  /* Populate the first group ID and name. */
1509
0
  if (gids != NULL) {
1510
0
    *((gid_t *) push_array(gids)) = pw->pw_gid;
1511
0
  }
1512
1513
0
  if (groups != NULL &&
1514
0
      (gr = my_getgrgid(pw->pw_gid)) != NULL) {
1515
0
    *((char **) push_array(groups)) = pstrdup(session.pool, gr->gr_name);
1516
0
  }
1517
1518
0
  my_setgrent();
1519
1520
  /* Myriad are the ways of obtaining the group membership of a user. */
1521
1522
0
  res = get_groups_by_initgroups(name, pw->pw_gid, gids, groups, my_getgrgid);
1523
0
  if (res < 0 &&
1524
0
      errno == ENOSYS) {
1525
0
    res = get_groups_by_getgrouplist(name, pw->pw_gid, gids, groups,
1526
0
      my_getgrgid);
1527
0
  }
1528
1529
0
  if (res < 0 &&
1530
0
      errno == ENOSYS) {
1531
0
    res = get_groups_by_getgrset(name, pw->pw_gid, gids, groups, my_getgrgid);
1532
0
  }
1533
1534
0
  if (res < 0 &&
1535
0
      errno == ENOSYS) {
1536
0
    res = get_groups_by_getgrent(name, pw->pw_gid, gids, groups, my_getgrent);
1537
0
  }
1538
1539
0
  if (res < 0) {
1540
0
    return PR_DECLINED(cmd);
1541
0
  }
1542
1543
0
  if (gids != NULL &&
1544
0
      gids->nelts > 0) {
1545
0
    return mod_create_data(cmd, (void *) &gids->nelts);
1546
0
  }
1547
1548
0
  if (groups != NULL &&
1549
0
      groups->nelts > 0) {
1550
0
    return mod_create_data(cmd, (void *) &groups->nelts);
1551
0
  }
1552
1553
0
  return PR_DECLINED(cmd);
1554
0
}
1555
1556
/* Configuration handlers
1557
 */
1558
1559
/* usage: AuthUnixOptions opt1 ... */
1560
0
MODRET set_authunixoptions(cmd_rec *cmd) {
1561
0
  config_rec *c;
1562
0
  register unsigned int i;
1563
0
  unsigned long opts = 0UL;
1564
1565
0
  if (cmd->argc == 1) {
1566
0
    CONF_ERROR(cmd, "wrong number of parameters");
1567
0
  }
1568
1569
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1570
1571
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1572
1573
0
  for (i = 1; i < cmd->argc; i++) {
1574
0
    if (strcasecmp(cmd->argv[i], "AIXNoRLogin") == 0) {
1575
0
      opts |= AUTH_UNIX_OPT_AIX_NO_RLOGIN;
1576
1577
0
    } else if (strcasecmp(cmd->argv[i], "NoGetgrouplist") == 0) {
1578
0
      opts |= AUTH_UNIX_OPT_NO_GETGROUPLIST;
1579
1580
0
    } else if (strcasecmp(cmd->argv[i], "NoInitgroups") == 0) {
1581
0
      opts |= AUTH_UNIX_OPT_NO_INITGROUPS;
1582
1583
0
    } else if (strcasecmp(cmd->argv[i], "MagicTokenChroot") == 0) {
1584
0
      opts |= AUTH_UNIX_OPT_MAGIC_TOKEN_CHROOT;
1585
1586
0
    } else if (strcasecmp(cmd->argv[i], "AIXNoAuthenticate") == 0) {
1587
0
      opts |= AUTH_UNIX_OPT_AIX_NO_AUTHENTICATE;
1588
1589
0
    } else {
1590
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown AuthUnixOption '",
1591
0
        cmd->argv[i], "'", NULL));
1592
0
    }
1593
0
  }
1594
1595
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
1596
0
  *((unsigned long *) c->argv[0]) = opts;
1597
1598
0
  return PR_HANDLED(cmd);
1599
0
}
1600
1601
0
MODRET set_persistentpasswd(cmd_rec *cmd) {
1602
0
  int persistence = -1;
1603
0
  config_rec *c;
1604
1605
0
  CHECK_ARGS(cmd, 1);
1606
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1607
1608
0
  persistence = get_boolean(cmd, 1);
1609
0
  if (persistence == -1) {
1610
0
    CONF_ERROR(cmd, "expected Boolean parameter");
1611
0
  }
1612
1613
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1614
0
  c->argv[0] = palloc(c->pool, sizeof(int));
1615
0
  *((int *) c->argv[0]) = persistence;
1616
1617
0
  return PR_HANDLED(cmd);
1618
0
}
1619
1620
/* Events handlers
1621
 */
1622
1623
0
static void auth_unix_exit_ev(const void *event_data, void *user_data) {
1624
0
  pr_auth_endpwent(session.pool);
1625
0
  pr_auth_endgrent(session.pool);
1626
0
}
1627
1628
0
static void auth_unix_sess_reinit_ev(const void *event_data, void *user_data) {
1629
0
  int res;
1630
1631
  /* A HOST command changed the main_server pointer, reinitialize ourselves. */
1632
1633
0
  pr_event_unregister(&auth_unix_module, "core.exit", auth_unix_exit_ev);
1634
0
  pr_event_unregister(&auth_unix_module, "core.session-reinit",
1635
0
    auth_unix_sess_reinit_ev);
1636
0
  auth_unix_opts = 0UL;
1637
0
  unix_persistent_passwd = FALSE;
1638
1639
0
  res = auth_unix_sess_init();
1640
0
  if (res < 0) {
1641
0
    pr_session_disconnect(&auth_unix_module,
1642
0
      PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
1643
0
  }
1644
0
}
1645
1646
/* Initialization routines
1647
 */
1648
1649
0
static int auth_unix_init(void) {
1650
1651
#ifdef HAVE__PW_STAYOPEN
1652
  _pw_stayopen = 1;
1653
#endif
1654
1655
0
  return 0;
1656
0
}
1657
1658
0
static int auth_unix_sess_init(void) {
1659
0
  config_rec *c;
1660
1661
0
  pr_event_register(&auth_unix_module, "core.exit", auth_unix_exit_ev, NULL);
1662
0
  pr_event_register(&auth_unix_module, "core.session-reinit",
1663
0
    auth_unix_sess_reinit_ev, NULL);
1664
1665
0
  c = find_config(main_server->conf, CONF_PARAM, "AuthUnixOptions", FALSE);
1666
0
  if (c != NULL) {
1667
0
    auth_unix_opts = *((unsigned long *) c->argv[0]);
1668
0
  }
1669
1670
0
  c = find_config(main_server->conf, CONF_PARAM, "PersistentPasswd", FALSE);
1671
0
  if (c != NULL) {
1672
0
    unix_persistent_passwd = *((int *) c->argv[0]);
1673
0
  }
1674
1675
0
  return 0;
1676
0
}
1677
1678
/* Module API tables
1679
 */
1680
1681
static conftable auth_unix_conftab[] = {
1682
  { "AuthUnixOptions",    set_authunixoptions,    NULL },
1683
  { "PersistentPasswd",   set_persistentpasswd,   NULL },
1684
  { NULL,     NULL,       NULL }
1685
};
1686
1687
static authtable auth_unix_authtab[] = {
1688
  { 0,  "setpwent", pw_setpwent },
1689
  { 0,  "endpwent", pw_endpwent },
1690
  { 0,  "setgrent",     pw_setgrent },
1691
  { 0,  "endgrent", pw_endgrent },
1692
  { 0,  "getpwent", pw_getpwent },
1693
  { 0,  "getgrent", pw_getgrent },
1694
  { 0,  "getpwnam", pw_getpwnam },
1695
  { 0,  "getpwuid", pw_getpwuid },
1696
  { 0,  "getgrnam",     pw_getgrnam },
1697
  { 0,  "getgrgid",     pw_getgrgid },
1698
  { 0,  "auth",         pw_auth },
1699
  { 0,  "authorize",  pw_authz },
1700
  { 0,  "check",  pw_check },
1701
  { 0,  "uid2name", pw_uid2name },
1702
  { 0,  "gid2name", pw_gid2name },
1703
  { 0,  "name2uid", pw_name2uid },
1704
  { 0,  "name2gid", pw_name2gid },
1705
  { 0,  "getgroups",  pw_getgroups },
1706
  { 0,  NULL }
1707
};
1708
1709
module auth_unix_module = {
1710
  NULL, NULL,
1711
1712
  /* Module API version */
1713
  0x20,
1714
1715
  /* Module name */
1716
  "auth_unix",
1717
1718
  /* Module configuration handler table */
1719
  auth_unix_conftab,
1720
1721
  /* Module command handler table */
1722
  NULL,
1723
1724
  /* Module authentication handler table */
1725
  auth_unix_authtab,
1726
1727
  /* Module initialization */
1728
  auth_unix_init,
1729
1730
  /* Session initialization */
1731
  auth_unix_sess_init
1732
};