1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <limits.h>
7 #include <sys/param.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <syslog.h>
12 
13 #include "config.h"
14 #include "pathnames.h"
15 #include "smbftpd.h"
16 #include "auth.h"
17 #include "restrict.h"
18 
19 extern smbftpd_conf_t smbftpd_conf;
20 extern smbftpd_share_t *smbftpd_shares;
21 
22 typedef	enum opt_type_t {
23 	OPT_TYPE_UNKNOWN = 0,
24 	OPT_TYPE_INT = 1,
25 	OPT_TYPE_OCTAL,
26 	OPT_TYPE_STR,
27 	OPT_TYPE_SET,
28 	OPT_TYPE_YES_NO,
29 	OPT_TYPE_MODE,
30 	OPT_TYPE_PATH,
31 	OPT_TYPE_SSL,
32 	OPT_TYPE_SPECIAL /* this is a special option type. The option have
33 	                   to assign 2 conf member or take special handling. */
34 } opt_type_t;
35 
36 /* List of all option in config file */
37 typedef struct conf_opt_list_t {
38 	char *opt_str;
39 	opt_type_t opt_type;
40 	void *smbftpd_conf_member;
41 } conf_opt_list_t;
42 
43 static conf_opt_list_t all_conf_list[] = {
44 	{"ChrootSet",            OPT_TYPE_SET,    &(smbftpd_conf.chroot_set)},
45 	{"MaxDownloadRate",      OPT_TYPE_SET,    &(smbftpd_conf.max_download_rate)},
46 	{"MaxUploadRate",        OPT_TYPE_SET,    &(smbftpd_conf.max_upload_rate)},
47 	{"ShareConfPath",        OPT_TYPE_PATH,   &(smbftpd_conf.share_conf_path)},
48 	{"TransferLog",          OPT_TYPE_STR,    &(smbftpd_conf.transfer_log_path)},
49 	{"ServerName",           OPT_TYPE_STR,    &(smbftpd_conf.server_name)},
50 	{"ListenOnAddress",      OPT_TYPE_STR,    &(smbftpd_conf.listen_on_address)},
51 	{"Port",                 OPT_TYPE_STR,    &(smbftpd_conf.port)},
52 	{"ForcePassiveIP",       OPT_TYPE_STR,    &(smbftpd_conf.force_passive_ip)},
53 	{"ExceptionList",        OPT_TYPE_STR,    &(smbftpd_conf.exception_list)},
54 	{"PidFile",              OPT_TYPE_STR,    &(smbftpd_conf.pid_file)},
55 	{"NoLoginList",          OPT_TYPE_STR,    &(smbftpd_conf.no_login_list)},
56 	{"VirtualUserMapping",   OPT_TYPE_STR,    &(smbftpd_conf.virtual_user_mapping)},
57 	{"VirtualUserAuthMethod",OPT_TYPE_STR,    &(smbftpd_conf.virtual_user_auth_method)},
58 	{"VirtualUserAuthConfig",OPT_TYPE_STR,    &(smbftpd_conf.virtual_user_auth_config)},
59 	{"CharsetEncoding",      OPT_TYPE_STR,    &(smbftpd_conf.charset_encoding)},
60 	{"DefaultMode",          OPT_TYPE_MODE,   &(smbftpd_conf.default_mode)},
61 	{"SupportUTF8Client",    OPT_TYPE_YES_NO, &(smbftpd_conf.support_utf8_client)},
62 	{"UsingUTF8FileSystem",  OPT_TYPE_YES_NO, &(smbftpd_conf.using_utf8_filesystem)},
63 	{"ShowProgramVersion",   OPT_TYPE_YES_NO, &(smbftpd_conf.show_program_version)},
64 	{"DebugMode",            OPT_TYPE_YES_NO, &(smbftpd_conf.debug_mode)},
65 	{"LogCommand",           OPT_TYPE_YES_NO, &(smbftpd_conf.log_command)},
66 	{"DoWtmpLog",            OPT_TYPE_YES_NO, &(smbftpd_conf.do_wtmp_log)},
67 	{"AnonymousLogin",       OPT_TYPE_YES_NO, &(smbftpd_conf.anonymous_login)},
68 	{"AnonymousOnly",        OPT_TYPE_YES_NO, &(smbftpd_conf.anonymous_only)},
69 	{"AnonymousReadOnly",    OPT_TYPE_YES_NO, &(smbftpd_conf.anonymous_readonly)},
70 	{"EmptyPasswdLogin",     OPT_TYPE_YES_NO, &(smbftpd_conf.empty_passwd_login)},
71 	{"ShowSymlinks",         OPT_TYPE_YES_NO, &(smbftpd_conf.show_symlinks)},
72 	{"ShowDotFiles",         OPT_TYPE_YES_NO, &(smbftpd_conf.show_dot_files)},
73 	{"RequireValidShell",    OPT_TYPE_YES_NO, &(smbftpd_conf.require_valid_shell)},
74 	{"DisableEPSV",          OPT_TYPE_YES_NO, &(smbftpd_conf.disable_epsv)},
75 	{"RestrictedPorts",      OPT_TYPE_YES_NO, &(smbftpd_conf.restricted_ports)},
76 	{"PassiveModePortRange", OPT_TYPE_SPECIAL, &(smbftpd_conf.passive_port_high)},
77 	{"Umask",                OPT_TYPE_OCTAL,  &(smbftpd_conf.umask)},
78 	{"MaxConnection",        OPT_TYPE_INT,    &(smbftpd_conf.max_connection)},
79 	{"MaxConnectionPerIP",   OPT_TYPE_INT,    &(smbftpd_conf.max_connection_per_ip)},
80 	{"TimeOut",              OPT_TYPE_INT,    &(smbftpd_conf.timeout)},
81 	{"MaxTimeOut",           OPT_TYPE_INT,    &(smbftpd_conf.max_timeout)},
82 
83 	// SSL/TLS Options
84 	{"SecurityPolicy",       OPT_TYPE_SSL,    &(smbftpd_conf.security_policy)},
85 	{"EncryptionType",       OPT_TYPE_SSL,    &(smbftpd_conf.encryption_type)},
86 	{"NormalUserMustSecure", OPT_TYPE_YES_NO, &(smbftpd_conf.normal_user_must_secure)},
87 	{"AnonymDisableSecure",  OPT_TYPE_YES_NO, &(smbftpd_conf.anonym_disable_secure)},
88 	{"SSLCertFile",          OPT_TYPE_PATH,   &(smbftpd_conf.ssl_cert_file)},
89 	{"SSLKeyFile",           OPT_TYPE_PATH,   &(smbftpd_conf.ssl_key_file)},
90 
91 	{NULL, 0, NULL}
92 };
93 
94 /**
95  * Initial the default value of smbconf
96  */
config_init()97 void config_init()
98 {
99 	char hostname[MAXHOSTNAMELEN];
100 
101 	bzero(hostname, sizeof(hostname));
102 	bzero(&smbftpd_conf, sizeof(smbftpd_conf));
103 	if (gethostname(hostname, sizeof(hostname)) == 0) {
104 		smbftpd_conf.server_name = strdup(hostname);
105 	} else {
106 		smbftpd_conf.server_name = strdup("Unkown");
107 	}
108 	smbftpd_conf.port = strdup("ftp");
109 	smbftpd_conf.umask = 022;
110 	smbftpd_conf.timeout = 900;
111 	smbftpd_conf.max_timeout = 7200;
112 	smbftpd_conf.show_program_version = 1;
113 	smbftpd_conf.default_mode = MODE_NORMAL;
114 	smbftpd_conf.restricted_ports = 1;
115 	smbftpd_conf.show_dot_files = 1;
116 	smbftpd_conf.require_valid_shell = 1;
117 	smbftpd_conf.no_login_list = strdup("500");
118 	smbftpd_conf.anonymous_readonly = 1;
119 
120 	// SSL/TLS options
121 	smbftpd_conf.security_policy = SECURITY_POLICY_NOSECURE;
122 	smbftpd_conf.encryption_type = ENCRYPTION_TYPE_SSL | ENCRYPTION_TYPE_TLS;
123 	smbftpd_conf.ssl_cert_file = strdup(PATH_SSL_CERT_FILE);
124 	smbftpd_conf.ssl_key_file = strdup(PATH_SSL_KEY_FILE);
125 }
126 
127 /**
128  * Option/value handler for smbftpd_config_parser() function.
129  *
130  * We will assign the option/value to smbftpd_conf
131  *
132  * @param option  Option name
133  * @param opt_arg Option value
134  *
135  * @return 0: Success
136  *         -1: Failed
137  */
smbftpd_config_handler(char * option,char * opt_arg)138 static int smbftpd_config_handler(char *option, char *opt_arg)
139 {
140 	conf_opt_list_t *opt_list = NULL;
141 	struct stat statBuf;
142 	char *szArg1, *szArg2;
143 	int error = -1, i;
144 
145 	for (i = 0; all_conf_list[i].opt_str; i++) {
146 		if (strcasecmp(option, all_conf_list[i].opt_str) == 0) {
147 			opt_list = &all_conf_list[i];
148 			break;
149 		}
150 	}
151 
152 	if (!opt_list) {
153 		syslog(LOG_ERR, "%s (%d) Skip unknown option %s", __FILE__, __LINE__, option);
154 		return 0;
155 	}
156 
157 	switch (opt_list->opt_type) {
158 	case OPT_TYPE_OCTAL:
159 		*((int *)(opt_list->smbftpd_conf_member)) = (int)strtol(opt_arg, (char **)NULL, 8);
160 		if (*((int *)(opt_list->smbftpd_conf_member)) <= 0) {
161 			syslog(LOG_ERR, "%s (%d) bad number of config option %s",
162 				   __FILE__, __LINE__, opt_list->opt_str);
163 			goto Error;
164 		}
165 		break;
166 	case OPT_TYPE_INT:
167 		*((int *)(opt_list->smbftpd_conf_member)) = (int)strtol(opt_arg, (char **)NULL, 10);
168 		if (*((int *)(opt_list->smbftpd_conf_member)) <= 0) {
169 			syslog(LOG_ERR, "%s (%d) bad number of config option %s",
170 				   __FILE__, __LINE__, opt_list->opt_str);
171 			goto Error;
172 		}
173 		break;
174 	case OPT_TYPE_SPECIAL:
175 		if (strcasecmp(option, "PassiveModePortRange") == 0) {
176 			int c;
177 			unsigned int lowport, highport;
178 
179 			c = sscanf(opt_arg, "%u-%u", &lowport, &highport);
180 			if (c != 2 || lowport < 1024U || highport > 65535U || highport < lowport) {
181 				syslog(LOG_ERR, "%s (%d) Bad port range %s", __FILE__, __LINE__, opt_arg);
182 				goto Error;
183 			}
184 			smbftpd_conf.passive_port_low = lowport;
185 			smbftpd_conf.passive_port_high = highport;
186 		}
187 		break;
188 	case OPT_TYPE_PATH:
189 		if (0 != stat(opt_arg, &statBuf) || !S_ISREG(statBuf.st_mode)) {
190 			syslog(LOG_ERR, "%s (%d) The option %s (%s) does not exist",
191 				   __FILE__, __LINE__, opt_list->opt_str, opt_arg);
192 			goto Error;
193 		}
194 		/* Fall down */
195 	case OPT_TYPE_STR:
196 		if (*((char **)opt_list->smbftpd_conf_member)) {
197 			free(*((char **)opt_list->smbftpd_conf_member));
198 		}
199 		*((char **)opt_list->smbftpd_conf_member) = strdup(opt_arg);
200 		if (*((char **)opt_list->smbftpd_conf_member) == NULL) {
201 			syslog(LOG_ERR, "%s (%d) Ran out of memory.", __FILE__, __LINE__);
202 			goto Error;
203 		}
204 		break;
205 	case OPT_TYPE_YES_NO:
206 		if (strcasecmp(opt_arg, "yes") == 0) {
207 			*((int *)opt_list->smbftpd_conf_member) = 1;
208 		} else {
209 			*((int *)opt_list->smbftpd_conf_member) = 0;
210 		}
211 		break;
212 	case OPT_TYPE_MODE:
213 		if (strcasecmp(opt_arg, "SMB") == 0) {
214 			*((int *)opt_list->smbftpd_conf_member) = 1;
215 		} else {
216 			*((int *)opt_list->smbftpd_conf_member) = 0;
217 		}
218 		break;
219 	case OPT_TYPE_SET:
220 		szArg1 = strtok(opt_arg, " \t");
221 		szArg2 = strtok(NULL, " \t");
222 		if (szArg1 && szArg2) {
223 			struct opt_set *p;
224 			p = calloc(sizeof(struct opt_set), 1);
225 			p->key = strdup(szArg1);
226 			p->value = strdup(szArg2);
227 			if (p->key == NULL || p->value == NULL) {
228 				syslog(LOG_ERR, "%s (%d) Ran out of memory.", __FILE__, __LINE__);
229 				goto Error;
230 			}
231 			p->next = *((struct opt_set **)opt_list->smbftpd_conf_member);
232 			*((struct opt_set **)opt_list->smbftpd_conf_member) = p;
233 		} else {
234 			syslog(LOG_ERR, "%s (%d) bad syntax of config option %s",
235 				   __FILE__, __LINE__, opt_list->opt_str);
236 			goto Error;
237 		}
238 		break;
239 	case OPT_TYPE_SSL:
240 		if (strcasecmp(opt_list->opt_str, "SecurityPolicy") == 0) {
241 			if (strcasecmp(opt_arg, "secure") == 0) {
242 				*((int *)opt_list->smbftpd_conf_member) = SECURITY_POLICY_SECURE;
243 			} else if (strcasecmp(opt_arg, "nosecure") == 0) {
244 				*((int *)opt_list->smbftpd_conf_member) = SECURITY_POLICY_NOSECURE;
245 			} else {
246 				*((int *)opt_list->smbftpd_conf_member) = SECURITY_POLICY_SECURE | SECURITY_POLICY_NOSECURE;
247 			}
248 		} else if (strcasecmp(opt_list->opt_str, "EncryptionType") == 0) {
249 			if (strcasecmp(opt_arg, "tls") == 0) {
250 				*((int *)opt_list->smbftpd_conf_member) = ENCRYPTION_TYPE_TLS;
251 			} else if (strcasecmp(opt_arg, "ssl") == 0) {
252 				*((int *)opt_list->smbftpd_conf_member) = ENCRYPTION_TYPE_SSL;
253 			} else {
254 				*((int *)opt_list->smbftpd_conf_member) = ENCRYPTION_TYPE_TLS | ENCRYPTION_TYPE_SSL;
255 			}
256 		} else {
257 			syslog(LOG_ERR, "%s (%d) bad syntax of config option %s",
258 				   __FILE__, __LINE__, opt_list->opt_str);
259 			goto Error;
260 		}
261 
262 		break;
263 	case OPT_TYPE_UNKNOWN:
264 		break;
265 	}
266 
267 	error = 0;
268 Error:
269 	return error;
270 }
271 
272 /**
273  * Read smbftpd.conf, smbftpd_share.conf, smbftpd_(auth mothod).conf.
274  * When failed, we will release the config that is read into memory.
275  *
276  * @param conf_path The path to smbftpd.conf
277  *
278  * @return 0: Success
279  *         -1: Failed
280  */
config_read(char * conf_path)281 int config_read(char *conf_path)
282 {
283 	int error = -1;
284 
285 	if (NULL == conf_path) {
286 		syslog(LOG_ERR, "%s (%d) Please specify the config file", __FILE__, __LINE__);
287 		goto Error;
288 	}
289 
290 	if (0 != smbftpd_config_parser(conf_path, smbftpd_config_handler)) {
291 		syslog(LOG_ERR, "%s (%d) Failed to parse %s", __FILE__, __LINE__, conf_path);
292 		goto Error;
293 	}
294 
295 	// Check and enum shares for SMB mode
296 	if (smbftpd_conf.default_mode == MODE_SMB ||
297 		(smbftpd_conf.default_mode == MODE_NORMAL && smbftpd_conf.exception_list)) {
298 		if (smbftpd_conf.share_conf_path == NULL) {
299 			syslog(LOG_ERR, "%s (%d) Enable SMB mode but failed to find samba config file.",
300 				   __FILE__, __LINE__);
301 			goto Error;
302 		} else if (0 != smbftpd_share_enum(smbftpd_conf.share_conf_path, &smbftpd_shares)) {
303 			syslog(LOG_ERR, "%s (%d) Failed to enumerate shares", __FILE__, __LINE__);
304 			goto Error;
305 		}
306 	}
307 
308 	// Check timeout and max timeout.
309 	if (smbftpd_conf.timeout > smbftpd_conf.max_timeout) {
310 		syslog(LOG_ERR, "%s (%d) The MaxTimeOut should be bigger than TimeOut.",
311 			   __FILE__, __LINE__);
312 		goto Error;
313 	}
314 
315 	// Check virtual user mapping config
316 	if (smbftpd_conf.virtual_user_mapping && (!smbftpd_conf.virtual_user_auth_method ||
317 											  !smbftpd_conf.virtual_user_auth_config)) {
318 		syslog(LOG_ERR, "%s (%d) VirtualUserMapping is set. But There is "
319 			   "no VirtualUserAuthMethod or VirtualUserAuthConfig", __FILE__, __LINE__);
320 		goto Error;
321 	}
322 
323 	// Parse authentication config file
324 	if (smbftpd_conf.virtual_user_mapping) {
325 		if (0 != smbftpd_auth_config_parse(smbftpd_conf.virtual_user_auth_method,
326 										    smbftpd_conf.virtual_user_auth_config)) {
327 			goto Error;
328 		}
329 	} else {
330 #ifdef	USE_PAM
331 		if (0 != smbftpd_auth_config_parse("pam", NULL)) {
332 			goto Error;
333 		}
334 #else
335 		if (0 != smbftpd_auth_config_parse("unix", NULL)) {
336 			goto Error;
337 		}
338 #endif
339 	}
340 
341 #ifdef	WITH_ICONV
342 	if (smbftpd_conf.support_utf8_client || smbftpd_conf.using_utf8_filesystem) {
343 		if (!smbftpd_conf.charset_encoding) {
344 			syslog(LOG_ERR, "%s (%d) In order to support UTF8 client/filesystem, you must set the CharsetEncoding.",
345 				   __FILE__, __LINE__);
346 			goto Error;
347 		}
348 	}
349 #endif
350 	// Open Unicode convert
351 	if (smbftpd_conf.charset_encoding) {
352 #ifdef	WITH_ICONV
353 		if (0 != smbftpd_unicode_open(smbftpd_conf.charset_encoding)) {
354 			goto Error;
355 		}
356 #else
357 		syslog(LOG_ERR, "%s (%d) iconv is not supported. Disable CharsetEncoding.", __FILE__, __LINE__);
358 #endif
359 	}
360 
361 	if (smbftpd_conf.max_connection && smbftpd_conf.max_connection_per_ip) {
362 		if (0 != smbftpd_iptrack_alloc(smbftpd_conf.max_connection)) {
363 			syslog(LOG_ERR, "%s (%d) Failed to alloc space for max connection per ip table.", __FILE__, __LINE__);
364 			goto Error;
365 		}
366 	}
367 
368 	error = 0;
369 Error:
370 	if (error != 0) {
371 		config_release();
372 	}
373 	return error;
374 }
375 
376 
377 /**
378  * Free the struct opt_set
379  *
380  * @param pSet
381  */
opt_set_free(struct opt_set * pSet)382 static void opt_set_free(struct opt_set *pSet)
383 {
384 	while (pSet) {
385 		struct opt_set *p = pSet;
386 		pSet = p->next;
387 		if (p->key) {
388 			free(p->key);
389 		}
390 		if (p->value) {
391 			free(p->value);
392 		}
393 		free(p);
394 	}
395 }
396 
397 /**
398  * Free the smbftpd_conf. We will call smbftpd_auth_config_free() and
399  * smbftpd_share_free() to free authenticaion method and smbftpd_shares;
400  */
config_release()401 void config_release()
402 {
403 	conf_opt_list_t *conf = all_conf_list;
404 
405 	while (conf->opt_str) {
406 		switch (conf->opt_type) {
407 		case OPT_TYPE_SET:
408 			opt_set_free(*((struct opt_set **)conf->smbftpd_conf_member));
409 			*(char **)conf->smbftpd_conf_member = NULL;
410 			break;
411 		case OPT_TYPE_PATH:
412 		case OPT_TYPE_STR:
413 			if (*(char **)conf->smbftpd_conf_member != NULL) {
414 				free(*((char **)conf->smbftpd_conf_member));
415 				*(char **)conf->smbftpd_conf_member = NULL;
416 			}
417 			break;
418 		default:
419 			if (conf->smbftpd_conf_member) {
420 				*(int **)conf->smbftpd_conf_member = 0;
421 			}
422 			break;
423 		}
424 		conf++;
425 	}
426 
427 	smbftpd_share_free(&smbftpd_shares);
428 
429 	bzero(&smbftpd_conf, sizeof(smbftpd_conf));
430 
431 	smbftpd_auth_config_free();
432 	smbftpd_share_free(&smbftpd_shares);
433 #ifdef	WITH_ICONV
434 	smbftpd_unicode_close();
435 #endif
436 	smbftpd_iptrack_free();
437 }
438 
439