1 #include <config.h>
2 #include <stdio.h>
3 #include <pwd.h>
4 #ifdef HAVE_SHADOW_H
5 #include <shadow.h>
6 #endif
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <string.h>
12 /* add BSD support */
13 #include <limits.h>
14
15 #ifdef HAVE_CRYPT_H
16 #include <crypt.h>
17 #endif
18 #ifdef WANT_PAM
19 #include <security/pam_appl.h>
20 #endif
21 #ifdef HAVE_UTMP_H
22 # include <utmp.h>
23 # ifdef HAVE_PATHS_H
24 # include <paths.h>
25 # ifndef _PATH_WTMP
26 # define _PATH_WTMP "/dev/null"
27 # warning "<paths.h> doesn't set _PATH_WTMP. You can not use wtmp logging"
28 # warning "with bftpd."
29 # endif
30 # else
31 # define _PATH_WTMP "/dev/null"
32 # warning "<paths.h> was not found. You can not use wtmp logging with bftpd."
33 # endif
34 #endif
35 #include <errno.h>
36 #include <grp.h>
37 #include <stdlib.h>
38 #ifdef HAVE_SYS_TIME_H
39 #include <sys/time.h>
40 #endif
41 #ifdef HAVE_TIME_H
42 #include <time.h>
43 #endif
44
45 #include "cwd.h"
46 #include "dirlist.h"
47 #include "mystring.h"
48 #include "options.h"
49 #include "login.h"
50 #include "logging.h"
51 #include "bftpdutmp.h"
52 #include "main.h"
53
54 #ifdef WANT_PAM
55 char usepam = 0;
56 pam_handle_t *pamh = NULL;
57 #endif
58
59 #ifdef HAVE_UTMP_H
60 FILE *wtmp;
61 #endif
62
63 struct passwd userinfo;
64 char userinfo_set = 0;
65
mygetpwuid(int uid,FILE * file,char * name)66 char *mygetpwuid(int uid, FILE * file, char *name)
67 {
68 int _uid;
69 char foo[256];
70 int i;
71 if (file) {
72 rewind(file);
73 while (fscanf(file, "%255s%*[^\n]\n", foo) != EOF) {
74 if ((foo[0] == '#') || (!strchr(foo, ':')) || (strchr(foo, ':') > foo + USERLEN - 1))
75 continue;
76 i = strchr(foo, ':') - foo;
77 strncpy(name, foo, i);
78 name[i] = 0;
79 sscanf(strchr(foo + i + 1, ':') + 1, "%i", &_uid);
80 if (_uid == uid) {
81 if (name[0] == '\n')
82 cutto(name, 1);
83 return name;
84 }
85 }
86 }
87 sprintf(name, "%i", uid);
88 return name;
89 }
90
mygetpwnam(char * name,FILE * file)91 int mygetpwnam(char *name, FILE * file)
92 {
93 char _name[USERLEN + 1];
94 char foo[256];
95 int uid, i;
96 if (file) {
97 rewind(file);
98 while (fscanf(file, "%255s%*[^\n]\n", foo) != EOF) {
99 if ((foo[0] == '#') || (!strchr(foo, ':')) || (strchr(foo, ':') > foo + USERLEN - 1))
100 continue;
101 i = strchr(foo, ':') - foo;
102 strncpy(_name, foo, i);
103 _name[i] = 0;
104 sscanf(strchr(foo + i + 1, ':') + 1, "%i", &uid);
105 if (_name[0] == '\n')
106 cutto(_name, 1);
107 if (!strcmp(name, _name))
108 return uid;
109 }
110 }
111 return -1;
112 }
113
114 #ifdef HAVE_UTMP_H
wtmp_init()115 void wtmp_init()
116 {
117 if (strcasecmp(config_getoption("LOG_WTMP"), "no")) {
118 if (!((wtmp = fopen(_PATH_WTMP, "a"))))
119 bftpd_log("Warning: Unable to open %s.\n", _PATH_WTMP);
120 }
121 }
122
bftpd_logwtmp(char type)123 void bftpd_logwtmp(char type)
124 {
125 struct utmp ut;
126 #ifndef __minix
127 struct timeval tv;
128 #endif
129 if (!wtmp)
130 return;
131 memset((void *) &ut, 0, sizeof(ut));
132 #ifdef _HAVE_UT_PID
133 ut.ut_pid = getpid();
134 #endif
135 sprintf(ut.ut_line, "ftp%i", (int) getpid());
136 if (type) {
137 #ifdef _HAVE_UT_TYPE
138 ut.ut_type = USER_PROCESS;
139 #endif
140 strncpy(ut.ut_name, user, sizeof(ut.ut_name));
141 #ifdef _HAVE_UT_HOST
142 strncpy(ut.ut_host, remotehostname, sizeof(ut.ut_host));
143 #endif
144 } else {
145 #ifdef _HAVE_UT_TYPE
146 ut.ut_type = DEAD_PROCESS;
147 #endif
148 }
149 /*
150 Using time() here is not strictly 64-bit compatible.
151 Will use timeval structure to get time instead.
152 time(&(ut.ut_time));
153 */
154 #if !defined(__minix) && !defined(__NetBSD__) && !defined(__DragonFly__)
155 gettimeofday(&tv, NULL);
156 ut.ut_tv.tv_sec = tv.tv_sec;
157 ut.ut_tv.tv_usec = tv.tv_usec;
158 #else
159 time(&(ut.ut_time));
160 #endif
161
162 fseek(wtmp, 0, SEEK_END);
163 fwrite((void *) &ut, sizeof(ut), 1, wtmp);
164 fflush(wtmp);
165 }
166
wtmp_end()167 void wtmp_end()
168 {
169 if (wtmp) {
170 if (state >= STATE_AUTHENTICATED)
171 bftpd_logwtmp(0);
172 fclose(wtmp);
173 }
174 }
175 #endif
176
login_init()177 void login_init()
178 {
179 char *foo = config_getoption("INITIAL_CHROOT");
180 #ifdef HAVE_UTMP_H
181 wtmp_init();
182 #endif
183 if (foo[0]) { /* Initial chroot */
184 if (chroot(foo) == -1) {
185 control_printf(SL_FAILURE, "421 Initial chroot failed.\r\n.");
186 exit(1);
187 }
188 }
189 }
190
bftpd_setuid(uid_t uid)191 int bftpd_setuid(uid_t uid)
192 {
193 /* If we must open the data connections from port 20,
194 * we have to keep the possibility to regain root privileges */
195 if (!strcasecmp(config_getoption("DATAPORT20"), "yes"))
196 return seteuid(uid);
197 else
198 return setuid(uid);
199 }
200
201
202 /*
203 Returns 0 on success and non-zero (-1) on failure
204 */
bftpd_login(char * password)205 int bftpd_login(char *password)
206 {
207 char str[MAX_STRING_LENGTH + 1];
208 char *foo;
209 int maxusers;
210 char *file_auth; /* if used, points to file used to auth users */
211 char *home_directory = NULL; /* retrieved from auth_file */
212 char *anonymous = NULL;
213 char *change_uid_text = NULL;
214 char *time_zone = NULL;
215 unsigned long get_maxusers;
216 int anon_ok = FALSE;
217 int change_uid = FALSE;
218
219 str[0] = '\0'; /* avoid garbage in str */
220 file_auth = config_getoption("FILE_AUTH");
221 anonymous = config_getoption("ANONYMOUS_USER");
222 change_uid_text = config_getoption("CHANGE_UID");
223
224 if (! strcasecmp(anonymous, "yes") )
225 {
226 anon_ok = TRUE;
227 }
228
229 if (! strcasecmp(change_uid_text, "yes") )
230 {
231 change_uid = TRUE;
232 }
233
234 time_zone = config_getoption("TIMEZONE_FIX");
235 if (! strcasecmp(time_zone, "no") )
236 {
237 /* we do not need the time zone fix, so do nothing here */
238 }
239 else
240 Get_Time_Zone_Difference();
241 if (! file_auth[0] ) /* not using auth file */
242 {
243 /* check to see if regular authentication is avail */
244 if ( anon_ok && ! change_uid )
245 {
246 home_directory = "/";
247 }
248 #ifndef NO_GETPWNAM
249 else if (!getpwnam(user)) {
250 control_printf(SL_FAILURE, "530 Login incorrect.");
251 // exit(0);
252 return -1;
253 }
254 #endif
255 }
256 /* we are using auth_file */
257 else
258 {
259 home_directory = check_file_password(file_auth, user, password);
260 if (! home_directory)
261 {
262 if ( anon_ok && ! change_uid )
263 home_directory = "/";
264 else
265 {
266 control_printf(SL_FAILURE, "530 Anonymous user not allowed.");
267 //exit(0);
268 return -1;
269 }
270 }
271 }
272
273 if (strncasecmp(foo = config_getoption("DENY_LOGIN"), "no", 2)) {
274 if (foo[0] != '\0') {
275 if (strncasecmp(foo, "yes", 3))
276 control_printf(SL_FAILURE, "530 %s", foo);
277 else
278 control_printf(SL_FAILURE, "530 Login incorrect.");
279 bftpd_log("Login as user '%s' failed: Server disabled.\n", user);
280 // exit(0);
281 return -1;
282 }
283 }
284 get_maxusers = strtoul(config_getoption("USERLIMIT_GLOBAL"), NULL, 10);
285 if (get_maxusers <= INT_MAX)
286 maxusers = get_maxusers;
287 else
288 {
289 bftpd_log("Error getting max users for GLOBAL in bftpd_login.\n", 0);
290 maxusers = 0;
291 }
292 if ((maxusers) && (maxusers == bftpdutmp_usercount("*"))) {
293 control_printf(SL_FAILURE, "421 There are already %i users logged in.", maxusers);
294 bftpd_log("Login as user '%s' failed. Too many users on server.\n", user);
295 exit(0);
296 }
297 get_maxusers = strtoul(config_getoption("USERLIMIT_SINGLEUSER"), NULL, 10);
298 if (get_maxusers <= INT_MAX)
299 maxusers = get_maxusers;
300 else
301 {
302 bftpd_log("error getting max users (SINGLE USER) in bftpd_login.\n", 0);
303 maxusers = 0;
304 }
305 if ((maxusers) && (maxusers == bftpdutmp_usercount(user))) {
306 control_printf(SL_FAILURE, "421 User %s is already logged in %i times.", user, maxusers);
307 bftpd_log("Login as user '%s' failed. Already logged in %d times.", maxusers);
308 exit(0);
309 }
310
311 /* Check to see if we should block multiple logins from the same machine.
312 -- Jesse <slicer69@hotmail.com>
313 */
314 get_maxusers = strtoul( config_getoption("USERLIMIT_HOST"), NULL, 10);
315 if (get_maxusers <= INT_MAX)
316 maxusers = get_maxusers;
317 else
318 {
319 bftpd_log("Error getting max users per HOST in bftpd_login.\n", 0);
320 maxusers = 0;
321 }
322
323 if ( (maxusers) && (maxusers == bftpdutmp_dup_ip_count(remotehostname) ) )
324 {
325 control_printf(SL_FAILURE, "421 Too many connections from your IP address.");
326 bftpd_log("Login as user '%s' failed. Already %d connections from %s.\n", user, maxusers, remotehostname);
327 exit(0);
328 }
329
330 /* disable these checks when logging in via auth file */
331 if ( (! file_auth[0] ) && (!anon_ok || change_uid) )
332 {
333 #ifndef NO_GETPWNAM
334 if(checkuser() || checkshell()) {
335 control_printf(SL_FAILURE, "530 Login incorrect.");
336 // exit(0);
337 return -1;
338 }
339 #endif
340 }
341
342 /* do not do this check when we are using auth_file */
343 if ( (! file_auth[0] ) && (! anon_ok) )
344 {
345 #ifndef NO_GETPWNAM
346 if (checkpass(password))
347 {
348 control_printf(SL_FAILURE, "530 Login incorrect.");
349 return -1;
350 }
351 #endif
352 }
353
354 if (strcasecmp((char *) config_getoption("RATIO"), "none")) {
355 sscanf((char *) config_getoption("RATIO"), "%i/%i",
356 &ratio_send, &ratio_recv);
357 }
358
359 /* do these checks if logging in via normal methods */
360 if ( (! file_auth[0]) && (!anon_ok || (anon_ok && change_uid)) )
361 {
362 strcpy(str, config_getoption("ROOTDIR"));
363 if (!str[0])
364 strcpy(str, "%h");
365 replace(str, "%u", userinfo.pw_name, MAX_STRING_LENGTH);
366 replace(str, "%h", userinfo.pw_dir, MAX_STRING_LENGTH);
367 if (!strcasecmp(config_getoption("RESOLVE_UIDS"), "yes"))
368 {
369 passwdfile = fopen("/etc/passwd", "r");
370 if (! passwdfile)
371 control_printf(SL_FAILURE, "421 Unable to open passwd file.\r\n%s.",
372 strerror(errno));
373 groupfile = fopen("/etc/group", "r");
374 if (! groupfile)
375 control_printf(SL_FAILURE, "421 Unable to open group file.\r\n%s.",
376 strerror(errno));
377 }
378
379 if ( setgid(userinfo.pw_gid) < 0 )
380 {
381 control_printf(SL_FAILURE, "421 Unable to change group.\r\n");
382 exit(0);
383 }
384 initgroups(userinfo.pw_name, userinfo.pw_gid);
385 if (strcasecmp(config_getoption("DO_CHROOT"), "no")) {
386 if (chroot(str)) {
387 control_printf(SL_FAILURE, "421 Unable to change root directory.\r\n%s.",
388 strerror(errno));
389 exit(0);
390 }
391 if (bftpd_setuid(userinfo.pw_uid)) {
392 control_printf(SL_FAILURE, "421 Unable to change uid.\r\n");
393 exit(0);
394 }
395 if (chdir("/")) {
396 control_printf(SL_FAILURE, "421 Unable to change working directory.\r\n%s.",
397 strerror(errno));
398 exit(0);
399 }
400 } else {
401 if (bftpd_setuid(userinfo.pw_uid)) {
402 control_printf(SL_FAILURE, "421 Unable to change uid.\r\n");
403 exit(0);
404 }
405 if (chdir(str)) {
406 control_printf(SL_FAILURE, "230 Couldn't change cwd to '%s': %s.\r\n", str,
407 strerror(errno));
408 if (chdir("/") == -1)
409 control_printf(SL_FAILURE, "421 Unable to change working directory.\r\n%s.",
410 strerror(errno));
411
412 }
413 }
414
415 } /* end of if we are using regular authentication methods */
416
417 /* perhaps we are using anonymous logins, but not file_auth? */
418 else if ( (! file_auth[0]) && (anon_ok) && ! change_uid )
419 {
420 strcpy(str, config_getoption("ROOTDIR"));
421 if (! str[0])
422 str[0] = '/';
423 replace(str, "%h", home_directory, MAX_STRING_LENGTH);
424 replace(str, "%u", user, MAX_STRING_LENGTH);
425 /* should we chroot? */
426 if ( strcasecmp(config_getoption("DO_CHROOT"), "no") )
427 {
428 if ( chroot(str) )
429 {
430 control_printf(SL_FAILURE, "421 Unable to change root directory.\r\n");
431 exit(0);
432 }
433 if ( chdir("/") )
434 {
435 control_printf(SL_FAILURE, "421 Unable to change working directory.\r\n");
436 exit(0);
437 }
438 }
439
440 } /* end of using anonymous login */
441 else /* we are using file authentication */
442 {
443 /* get home directory */
444 strcpy(str, config_getoption("ROOTDIR"));
445 if (! str[0])
446 strcpy(str, "%h");
447 replace(str, "%h", home_directory, MAX_STRING_LENGTH);
448 replace(str, "%u", user, MAX_STRING_LENGTH);
449
450 /* see if we should change root */
451 if ( strcasecmp(config_getoption("DO_CHROOT"), "no") )
452 {
453 if ( chroot(home_directory) )
454 {
455 control_printf(SL_FAILURE, "421 Unable to change root directory.\r\n");
456 exit(0);
457 }
458 if ( chdir("/") )
459 {
460 control_printf(SL_FAILURE, "421 Unable to change working directory.\r\n");
461 exit(0);
462 }
463 }
464
465 } /* end of using file auth */
466
467 new_umask();
468 /* print_file(230, config_getoption("MOTD_USER")); */
469 strcpy(str, config_getoption("MOTD_USER"));
470 /* Allow user specific path to MOTD file. */
471 replace(str, "%h", home_directory, MAX_STRING_LENGTH);
472 replace(str, "%u", user, MAX_STRING_LENGTH);
473 print_file(230, str);
474
475 control_printf(SL_SUCCESS, "230 User logged in.");
476 #ifdef HAVE_UTMP_H
477 bftpd_logwtmp(1);
478 #endif
479 bftpdutmp_log(1);
480 bftpd_log("Successfully logged in as user '%s'.\n", user);
481 if (config_getoption("AUTO_CHDIR")[0])
482 if ( chdir(config_getoption("AUTO_CHDIR")) == -1)
483 bftpd_log("Chdir failed: %s.\n", strerror(errno));
484
485 state = STATE_AUTHENTICATED;
486 bftpd_cwd_init();
487
488 /* a little clean up before we go */
489 if ( (home_directory) && ( strcmp(home_directory, "/" ) ) )
490 free(home_directory);
491
492 return 0;
493 }
494
495
496 /* Return 1 on failure and 0 on success. */
checkpass(char * password)497 int checkpass(char *password)
498 {
499 #ifndef NO_GETPWNAM
500 if (!getpwnam(user))
501 return 1;
502 #endif
503
504 if (!strcasecmp(config_getoption("ANONYMOUS_USER"), "yes"))
505 return 0;
506
507 #ifdef WANT_PAM
508 if (!strcasecmp(config_getoption("AUTH"), "pam"))
509 return checkpass_pam(password);
510 else
511 #endif
512 return checkpass_pwd(password);
513 }
514
515
516
login_end()517 void login_end()
518 {
519 #ifdef WANT_PAM
520 if (usepam)
521 return end_pam();
522 #endif
523 #ifdef HAVE_UTMP_H
524 wtmp_end();
525 #endif
526 }
527
528
529 /*
530 Notes for this function.
531 1. Returned values fom crypt() should not be freed, it
532 causes a segfault.
533 2. Values returned from crypt() cannot be assumed to be
534 valid and need to be checked.
535 3. This function returns 0 on success and 1 on error.
536 -- Jesse
537 */
checkpass_pwd(char * password)538 int checkpass_pwd(char *password)
539 {
540 char *crypt_value = NULL;
541 #ifdef HAVE_SHADOW_H
542 char *shadow_value = NULL;
543 #endif
544 #ifdef HAVE_SHADOW_H
545 struct spwd *shd;
546 #endif
547 crypt_value = crypt(password, userinfo.pw_passwd);
548 // if (strcmp(userinfo.pw_passwd, (char *) crypt(password, userinfo.pw_passwd))) {
549 if (!crypt_value || strcmp(userinfo.pw_passwd, crypt_value) )
550 {
551 #ifdef HAVE_SHADOW_H
552 if (!(shd = getspnam(user)))
553 return 1;
554 shadow_value = crypt(password, shd->sp_pwdp);
555 if (! shadow_value)
556 return 1;
557 // if (strcmp(shd->sp_pwdp, (char *) crypt(password, shd->sp_pwdp)))
558 if ( strcmp(shd->sp_pwdp, shadow_value) )
559 #endif
560 return 1;
561 }
562 return 0;
563 }
564
565 #ifdef WANT_PAM
conv_func(int num_msg,const struct pam_message ** msgm,struct pam_response ** resp,void * appdata_ptr)566 int conv_func(int num_msg, const struct pam_message **msgm,
567 struct pam_response **resp, void *appdata_ptr)
568 {
569 struct pam_response *response;
570 int i;
571 response = (struct pam_response *) malloc(sizeof(struct pam_response) * num_msg);
572 for (i = 0; i < num_msg; i++) {
573 response[i].resp = (char *) strdup(appdata_ptr);
574 response[i].resp_retcode = 0;
575 }
576 *resp = response;
577 return 0;
578 }
579
checkpass_pam(char * password)580 int checkpass_pam(char *password)
581 {
582 struct pam_conv conv = { conv_func, password };
583 int retval = pam_start("bftpd", user, (struct pam_conv *) &conv,
584 (pam_handle_t **) & pamh);
585 if (retval != PAM_SUCCESS) {
586 printf("Error while initializing PAM: %s\n",
587 pam_strerror(pamh, retval));
588 return 1;
589 }
590 /*
591 Allow Bftpd to build with OpenPAM
592 pam_fail_delay(pamh, 0);
593 */
594 retval = pam_authenticate(pamh, 0);
595 if (retval == PAM_SUCCESS)
596 retval = pam_acct_mgmt(pamh, 0);
597 if (retval == PAM_SUCCESS)
598 pam_open_session(pamh, 0);
599 if (retval != PAM_SUCCESS)
600 return 1;
601 else
602 return 0;
603 }
604
end_pam()605 void end_pam()
606 {
607 if (pamh) {
608 pam_close_session(pamh, 0);
609 pam_end(pamh, 0);
610 }
611 }
612 #endif
613
checkuser()614 int checkuser()
615 {
616
617 FILE *fd;
618 char *p;
619 char line[256];
620
621 if ((fd = fopen(config_getoption("PATH_FTPUSERS"), "r"))) {
622 while (fgets(line, sizeof(line), fd))
623 if ((p = strchr(line, '\n'))) {
624 *p = '\0';
625 if (line[0] == '#')
626 continue;
627 if (!strcasecmp(line, user)) {
628 fclose(fd);
629 return 1;
630 }
631 }
632 fclose(fd);
633 }
634 return 0;
635 }
636
637 /*
638 Returns zero when everything is okay and 1 to
639 indicate an error or lack of shell.
640 */
checkshell()641 int checkshell()
642 {
643 #ifdef HAVE_GETUSERSHELL
644 char *cp;
645 struct passwd *pwd;
646
647 if (!strcasecmp(config_getoption("AUTH_ETCSHELLS"), "no"))
648 return 0;
649
650 pwd = getpwnam(user);
651 /* make sure we do not try to use an invalid return */
652 if (! pwd)
653 return 1;
654 while ((cp = getusershell()))
655 if (!strcmp(cp, pwd->pw_shell))
656 break;
657 endusershell();
658
659 if (!cp)
660 return 1;
661 else
662 return 0;
663 #else
664 return 0;
665 # warning "Your system doesn't have getusershell(). You can not"
666 # warning "use /etc/shells authentication with bftpd."
667 #endif
668 }
669
670
671
672
673 /*
674 This function searches through a text file for a matching
675 username. If a match is found, the password in the
676 text file is compared to the password passed in to
677 the function. If the password matches, the function
678 returns the fourth field (home directory). On failure,
679 it returns NULL.
680 -- Jesse
681 */
check_file_password(char * my_filename,char * my_username,char * my_password)682 char *check_file_password(char *my_filename, char *my_username, char *my_password)
683 {
684 FILE *my_file;
685 int found_user = 0;
686 char user[33], password[33], group[33], home_dir[65];
687 char *my_home_dir = NULL;
688 int return_value;
689
690 my_file = fopen(my_filename, "r");
691 if (! my_file)
692 return NULL;
693
694 return_value = fscanf(my_file, "%32s %32s %32s %64s", user, password, group, home_dir);
695 if (! strcmp(user, my_username) )
696 found_user = 1;
697
698 while ( (! found_user) && ( return_value != EOF) )
699 {
700 return_value = fscanf(my_file, "%32s %32s %32s %64s", user, password, group, home_dir);
701 if (! strcmp(user, my_username) )
702 found_user = 1;
703 }
704
705 fclose(my_file);
706 if (found_user)
707 {
708 /* check password */
709 if (! strcmp(password, "*") )
710 {
711 }
712 else if ( strcmp(password, my_password) )
713 return NULL;
714
715 my_home_dir = calloc( strlen(home_dir) + 1, sizeof(char) );
716 if (! my_home_dir)
717 return NULL;
718 strcpy(my_home_dir, home_dir);
719 }
720
721 return my_home_dir;
722 }
723
724