1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <syslog.h>
8 #include <string.h>
9 #include <paths.h>
10 #include <time.h>
11 #include <sys/stat.h>
12 #include <limits.h>
13 #include <grp.h>
14 #include <arpa/ftp.h>
15 
16 #include "pathnames.h"
17 #include "smbftpd.h"
18 #include "restrict.h"
19 #include "ssl.h"
20 #include "auth.h"
21 
22 extern smbftpd_share_t *smbftpd_shares;
23 extern smbftpd_conf_t smbftpd_conf;
24 extern smbftpd_session_t smbftpd_session;
25 
26 extern int login_attempts;
27 extern int askpasswd;
28 
29 #ifndef LINE_MAX
30 	#define LINE_MAX 2048
31 #endif
32 
33 /**
34  * Terminate login as previous user, if any, resetting state;
35  * Used when USER command is given or login fails.
36  */
end_login(void)37 void end_login(void)
38 {
39 	(void) seteuid(0);
40 	if (smbftpd_session.logged_in && smbftpd_conf.do_wtmp_log && !smbftpd_session.chroot)
41 		smbftpd_logwtmp(NULL, NULL); // We don't do logwtmp in chroot, because it won't work anyway
42 
43 
44 	if (smbftpd_conf.transfer_log_path) {
45 		smbftpd_xferlog_close();
46 	}
47 
48 	smbftpd_valid_share_free(&smbftpd_session.valid_shares);
49 	if (smbftpd_session.home) {
50 		free(smbftpd_session.home);
51 	}
52 	bzero(&smbftpd_session, sizeof(smbftpd_session));
53 }
54 
55 /**
56  * Performing password authentication. The USER command shoule send first.
57  *
58  * @param passwd
59  */
cmd_pass(const char * passwd)60 void cmd_pass(const char *passwd)
61 {
62 	int login_failed = 1;
63 	FILE *fd;
64 	char *chroot_dir = NULL, *home_dir = NULL;
65 	const char *dir = NULL;
66 
67 #ifdef WITH_SSL
68 	if (!smbftpd_session.ssl_ctrl.ssl_active_flag &&
69 		!(smbftpd_conf.security_policy & SECURITY_POLICY_NOSECURE)) {
70 		reply_noformat(504, "TLS/SSL protection required.");
71 		return;
72 	}
73 #endif /* WITH_SSL */
74 
75 	if (smbftpd_session.logged_in || askpasswd == 0) {
76 		reply_noformat(503, "Login with USER first.");
77 		return;
78 	}
79 
80 	askpasswd = 0;
81 	if (!smbftpd_session.guest) {
82 
83 		if (smbftpd_session.pw_user == NULL) {
84 			/* failure below */
85 			goto skip;
86 		}
87 
88 		if ((*passwd == '\0') && (smbftpd_conf.empty_passwd_login == 0)) {
89 			reply_noformat(530, "Empty password is not allowed.");
90 			if (login_attempts++ >= 5) {
91 				syslog(LOG_NOTICE,
92 					   "repeated login failures from %s",
93 					   smbftpd_session.remotehost);
94 				exit(0);
95 			}
96 			return;
97 		}
98 
99 		if (0 == smbftpd_auth_check(smbftpd_session.username, passwd)) {
100 			login_failed = 0;
101 		}
102 	skip:
103 		/*
104 		 * If login_failed == 1, the user failed the authentication check
105 		 * above.  If rval == 0, either PAM or local authentication
106 		 * succeeded.
107 		 */
108 		if (login_failed) {
109 			reply_noformat(530, "Login incorrect.");
110 			if (smbftpd_conf.log_command) {
111 				syslog(LOG_NOTICE,
112 					   "FTP LOGIN FAILED FROM %s",
113 					   smbftpd_session.remotehost);
114 				syslog(LOG_AUTHPRIV | LOG_NOTICE,
115 					   "FTP LOGIN FAILED FROM %s, %s",
116 					   smbftpd_session.remotehost, smbftpd_session.username);
117 			}
118 			smbftpd_session.pw_user = NULL;
119 			if (login_attempts++ >= 5) {
120 				syslog(LOG_NOTICE,
121 					   "repeated login failures from %s",
122 					   smbftpd_session.remotehost);
123 				exit(0);
124 			}
125 			return;
126 		}
127 	}
128 	login_attempts = 0;		/* this time successful */
129 	if (setegid(smbftpd_session.pw_user->pw_gid) < 0) {
130 		reply_noformat(550, "Can't set gid.");
131 		return;
132 	}
133 
134 	/* May be overridden by login.conf */
135 	(void) umask(smbftpd_conf.umask);
136 
137 #ifdef	__FreeBSD__
138 	setlogin(smbftpd_session.username);
139 #endif
140 	(void) initgroups(smbftpd_session.pw_user->pw_name, smbftpd_session.pw_user->pw_gid);
141 
142 	/* open xfer log before chroot */
143 	if (smbftpd_conf.transfer_log_path) {
144 		smbftpd_xferlog_open(smbftpd_conf.transfer_log_path);
145 	}
146 
147 	smbftpd_session.logged_in = 1;
148 	smbftpd_session.transfer_type = TYPE_I;
149 
150 	/* Get FTP configuration and limitions for current user*/
151 	smbftpd_session.mode = smbftpd_mode_get(smbftpd_conf.default_mode,
152 											smbftpd_conf.exception_list, smbftpd_session.username);
153 
154 	smbftpd_session.max_upload_rate = smbftpd_transfer_rate_get(smbftpd_conf.max_upload_rate,
155 																smbftpd_session.username);
156 	smbftpd_session.max_download_rate = smbftpd_transfer_rate_get(smbftpd_conf.max_download_rate,
157 																  smbftpd_session.username);
158 	smbftpd_valid_share_free(&smbftpd_session.valid_shares);
159 
160 	smbftpd_session.byte_uploaded = 0;
161 	smbftpd_session.byte_downloaded = 0;
162 
163 	if (smbftpd_session.guest) {
164 		home_dir = strdup(smbftpd_session.pw_user->pw_dir);
165 	} else {
166 		home_dir = smbftpd_auth_get_home(smbftpd_session.username);
167 	}
168 	if (home_dir == NULL) {
169 		reply_noformat(550, "Can't get home dir.");
170 		goto bad;
171 	}
172 
173 	if (smbftpd_session.guest && smbftpd_session.mode != MODE_SMB) {
174 		dir = smbftpd_session.pw_user->pw_dir;
175 	} else {
176 		dir = smbftpd_chroot_path_get(smbftpd_conf.chroot_set, smbftpd_session.username);
177 	}
178 	if (dir) {
179 		smbftpd_session.chroot = 1;
180 		smbftpd_session.mode = MODE_NORMAL;
181 
182 		if (dir[0] == '/') {
183 			chroot_dir = strdup(dir); /* so it can be freed */
184 		} else if (dir[0] == '~') {
185 			asprintf(&chroot_dir, "%s/%s", home_dir, dir+1);
186 		} else {
187 			asprintf(&chroot_dir, "%s/%s", home_dir, dir);
188 		}
189 		if (chroot_dir == NULL)
190 			fatalerror("Ran out of memory.");
191 
192 		free(home_dir);
193 		home_dir = NULL;
194 
195 		smbftpd_session.home = strdup("/");
196 		if (smbftpd_session.home == NULL) {
197 			fatalerror("Ran out of memory.");
198 		}
199 
200 		/*
201 		 * Finally, do chroot()
202 		 */
203 		if (chroot(chroot_dir) < 0) {
204 			reply_noformat(550, "Can't change root.");
205 			goto bad;
206 		}
207 	} else	{/* real user w/o chroot */
208 		if (smbftpd_session.mode == MODE_SMB) {
209 
210 			if (0 != smbftpd_valid_share_get(smbftpd_session.username, home_dir,
211 									smbftpd_shares, &smbftpd_session.valid_shares)) {
212 				fatalerror("Ran out of memory.");
213 			}
214 			free(home_dir);
215 			home_dir = NULL;
216 
217 			smbftpd_session.home = strdup(PATH_SMB_FTPD_ROOT);
218 		} else {
219 			smbftpd_session.home = home_dir;
220 		}
221 	}
222 	if (smbftpd_session.home == NULL) {
223 		fatalerror("Ran out of memory.");
224 	}
225 
226 	if (smbftpd_conf.do_wtmp_log && !smbftpd_session.chroot) {
227 		smbftpd_logwtmp(smbftpd_session.username, smbftpd_session.remotehost);
228 	}
229 
230 	/*
231 	 * Set euid *before* doing chdir() so
232 	 * a) the user won't be carried to a directory that he couldn't reach
233 	 *    on his own due to no permission to upper path components,
234 	 * b) NFS mounted homedirs w/restrictive permissions will be accessible
235 	 *    (uid 0 has no root power over NFS if not mapped explicitly.)
236 	 */
237 	if (seteuid(smbftpd_session.pw_user->pw_uid) < 0) {
238 		reply_noformat(550, "Can't set uid.");
239 		goto bad;
240 	}
241 
242 	if (chdir(smbftpd_session.home) < 0) {
243 		if (smbftpd_session.guest || smbftpd_session.chroot || smbftpd_session.mode == MODE_SMB) {
244 			reply_noformat(550, "Can't change to base directory.");
245 			goto bad;
246 		} else {
247 			if (chdir("/") < 0) {
248 				reply_noformat(550, "Root is inaccessible.");
249 				goto bad;
250 			}
251 			reply_noformat(LONG_REPLY(230), "No directory! Logging in with home=/.");
252 		}
253 	}
254 
255 	/*
256 	 * Display a login message, if it exists.
257 	 * N.B. reply(230,) must follow the message.
258 	 */
259 	fd = fopen(PATH_FTPLOGINMESG, "r");
260 	if (fd != NULL) {
261 		char *cp, line[LINE_MAX];
262 
263 		while (fgets(line, sizeof(line), fd) != NULL) {
264 			if ((cp = strchr(line, '\n')) != NULL)
265 				*cp = '\0';
266 			reply_noformat(LONG_REPLY(230), line);
267 		}
268 		(void) smbftpd_socket_fflush(stdout, 0);
269 		(void) fclose(fd);
270 	}
271 	if (smbftpd_session.guest) {
272 
273 		reply_noformat(230, "Guest login ok, access restrictions apply.");
274 
275 		proc_title_init("%s: anonymous/%s", smbftpd_session.remotehost, passwd);
276 
277 		if (smbftpd_conf.log_command)
278 			syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
279 				   smbftpd_session.remotehost, passwd);
280 	} else {
281 		if (smbftpd_session.chroot)
282 			reply(230, "User %s logged in, "
283 				  "access restrictions apply.", smbftpd_session.username);
284 		else
285 			reply(230, "User %s logged in.", smbftpd_session.username);
286 
287 		proc_title_init("%s: user/%s", smbftpd_session.remotehost, smbftpd_session.username);
288 
289 		if (smbftpd_conf.log_command)
290 			syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
291 				   smbftpd_session.remotehost, smbftpd_session.username);
292 	}
293 
294 	if (chroot_dir)
295 		free(chroot_dir);
296 	return;
297 	bad:
298 	/* Forget all about it... */
299 	if (chroot_dir)
300 		free(chroot_dir);
301 	end_login();
302 }
303