1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include <sys/param.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <syslog.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <pwd.h>
13 
14 #include "smbftpd.h"
15 #include "pathnames.h"
16 
17 #ifndef LINE_MAX
18 	#define LINE_MAX 2048
19 #endif
20 
21 extern smbftpd_session_t smbftpd_session;
22 
23 /**
24  * Get the real full path on the filesystem. If the path is a symbolic
25  * link, we will chdir to the path and use getcwd to get the realpath.
26  *
27  * We need to make sure we are not using symbolic path in the share's
28  * path. Otherwise, it will failed in smbftpd_get_realpath()
29  *
30  * @param path
31  *
32  * @return
33  */
real_full_path(const char * path)34 static char *real_full_path(const char *path)
35 {
36 	static char real_path[PATH_MAX];
37 	char curpath[PATH_MAX];
38 
39 	bzero(real_path, sizeof(real_path));
40 	bzero(curpath, sizeof(curpath));
41 
42 	getcwd(curpath, sizeof(curpath));
43 	if (chdir(path) == 0) {
44 		getcwd(real_path, sizeof(real_path));
45 		chdir(curpath);
46 		return real_path;
47 	} else {
48 		return NULL;
49 	}
50 }
51 
52 /**
53  * Find the share in validshares and return its path.
54  *
55  * @param validshares
56  *               The linked list of all valid shares
57  * @param share  The share name to look for
58  *
59  * @return Return path of share when the share is found in the validshares.
60  *         Return NULL if not found.
61  */
smbftpd_share_path_get(smbftpd_valid_share_t * validshares,const char * share)62 static char *smbftpd_share_path_get(smbftpd_valid_share_t *validshares, const char *share)
63 {
64 	smbftpd_valid_share_t *p;
65 	char *path = NULL;
66 
67 	if ((NULL == validshares) || (NULL == share)){
68 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
69 		return NULL;
70 	}
71 
72 	p = validshares;
73 	while (p) {
74 		if (strcmp(share, p->share) == 0) {
75 			path = p->path;
76 			break;
77 		}
78 		p = p->next;
79 	}
80 	return path;
81 }
82 
83 /**
84  * Check whether the path is one of the share path root in the
85  * validshares.
86  *
87  * We will traverse the validshares and compare the share path with path.
88  * When the path is the same, reture 1.
89  *
90  * @param validshares
91  *               The linked list of all valid shares
92  * @param path   The path to search
93  *
94  * @return 1: The path is a share root path
95  *         0: The path is not a share root path
96  */
is_share_root(smbftpd_valid_share_t * validshares,const char * path)97 static int is_share_root(smbftpd_valid_share_t *validshares, const char *path)
98 {
99 	smbftpd_valid_share_t *p;
100 
101 	if (NULL == path){
102 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
103 		return 0;
104 	}
105 
106 	p = validshares;
107 	while (p) {
108 		if (strcmp(path, p->path) == 0) {
109 			return 1;
110 		}
111 		p = p->next;
112 	}
113 	return 0;
114 }
115 
116 /**
117  * This function will replace the real path name to virtual path name
118  * in the given path.
119  *
120  * For example, if share "SHARE" has real path named "/volume1/share1",
121  * when user access path "/volume1/share1/abc/cde", we will replace the
122  * path to "/SHARE/abc/cde".
123  *
124  * @param validshares
125  *                The linked list of valid shares
126  * @param path    The current path. We will compare the path with path in the
127  *                validshares. If path matches, we will write new path in "path"
128  * @param bufsize The buffer length of path
129  *
130  * @return 0: Success
131  *         -1: Failed or path not found in validshares
132  */
smbfptd_replace_share_path(smbftpd_valid_share_t * validshares,char * path,int bufsize)133 int smbfptd_replace_share_path(smbftpd_valid_share_t *validshares, char *path, int bufsize)
134 {
135 	smbftpd_valid_share_t *p;
136 	char *slash;
137 	char buf[PATH_MAX];
138 
139 	if ( (path == NULL) || (bufsize == 0) ) {
140 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
141 		return -1;
142 	}
143 
144 	if (smbftpd_session.mode == MODE_NORMAL) {
145 		return 0;
146 	}
147 
148 	if (strcmp(path, PATH_SMB_FTPD_ROOT) == 0) {
149 		snprintf(path, bufsize, "/");
150 		return 0;
151 	}
152 
153 	snprintf(buf, sizeof(buf), "%s", path);
154 
155 	p = validshares;
156 	while (p) {
157 
158 		if (strncmp(p->path, path, strlen(p->path)) == 0) {
159 			slash = buf + strlen(p->path);
160 			if (*slash != '/' && *slash != '\0') {
161 				continue;
162 			}
163 
164 			snprintf(path, bufsize, "/%s%s", p->share, slash);
165 
166 			return 0;
167 		}
168 		p = p->next;
169 	}
170 
171 	return -1;
172 }
173 
174 /**
175  * Get the pointer of smbftpd_valid_share_t from the given
176  * validshares by path.
177  *
178  * The path is a real full path. We will compare the path with path of
179  * all shares in validshares. If path matches, reture the pointer of
180  * the smbftpd_valid_share_t
181  *
182  * @param validshares
183  *               The linked list of valid shares
184  * @param path   Real full path to check
185  *
186  * @return NULL if no match share
187  *         If found, return the valid share
188  *
189  *         Please note the returned valid share is a const pointer. It
190  *         is pointed to smbftpd_session.valid_shares.
191  */
smbftpd_get_share_by_path(smbftpd_valid_share_t * validshares,const char * path)192 const smbftpd_valid_share_t *smbftpd_get_share_by_path(smbftpd_valid_share_t *validshares, const char *path)
193 {
194 	smbftpd_valid_share_t *p;
195 	const char *slash;
196 
197 	// for each share, compare the path with the share path
198 	for (p = validshares; p; p = p->next) {
199 		if (!p->path) {
200 			continue;
201 		}
202 		if (strncmp(p->path, path, strlen(p->path)) == 0) {
203 			slash = path + strlen(p->path);
204 			if (*slash != '/' && *slash != '\0') {
205 				continue;
206 			}
207 			return p;
208 		}
209 	}
210 	return NULL;
211 }
212 
213 /**
214  * Check whether szPath is a valid path. We will go through the
215  * validshares and check whenther the path is under valid shares.
216  * When writable is not 0, we will also check the share must be
217  * writable.
218  *
219  * @param validshares
220  *                 The linked list of valid shares
221  * @param path     The path to check
222  * @param writable Check whether the share is writable
223  *
224  * @return 1: Yes
225  *         0: No or failed
226  */
smbftpd_is_under_valid_path(smbftpd_valid_share_t * validshares,const char * path,int writable)227 static int smbftpd_is_under_valid_path(smbftpd_valid_share_t *validshares, const char *path, int writable)
228 {
229 	const smbftpd_valid_share_t *share;
230 
231 	if (!path) {
232 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
233 		return 0;
234 	}
235 
236 	share = smbftpd_get_share_by_path(validshares, path);
237 	if (!share) {
238 		return 0;
239 	}
240 	if (writable && !share->writable) {
241 		return 0;
242 	}
243 
244 	return 1;
245 }
246 
247 /**
248  * This is the main path parse function.
249  *
250  * The function will return the real path in the system by parse
251  * the given path. We will chdir() into each component in the
252  * given path to make sure the path is valid.
253  *
254  * If flags & FLAG_CHECK_WRITABLE, than check whether path is writable.
255  * If flags & FLAG_NO_FOLLOW_LINK, then do not follow the link.
256  * If flags & FLAG_NO_FOLLOW_LAST_LINK, then we won't check the last
257  * component.
258  *
259  * @param validshares
260  *               The linked list of valid shares
261  * @param path   The path to convert
262  * @param flags  If flags & FLAG_CHECK_WRITABLE, than check whether path is writable.
263  *               If flags & FLAG_NO_FOLLOW_LINK, then do not follow the link.
264  *               If flags & FLAG_NO_FOLLOW_LAST_LINK, then we won't check the last
265  *               component.
266  *
267  * @return Real path if the path is valid.
268  *         NULL if the path is not allowed
269  */
smbftpd_get_realpath(smbftpd_valid_share_t * validshares,const char * path,int flags)270 char *smbftpd_get_realpath(smbftpd_valid_share_t *validshares, const char *path, int flags)
271 {
272 	static char return_path[PATH_MAX+1];
273 	char	orig_pwd[PATH_MAX+1], curdir[PATH_MAX+1];
274 	char	token_path[PATH_MAX+1], tmp_path[PATH_MAX+1];
275 	char	*token;
276 	struct	stat statbuf;
277 	int err;
278 
279 	if (!path) {
280 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
281 		return NULL;
282 	}
283 
284 	if (smbftpd_session.mode == MODE_NORMAL) {
285 		if (flags & FLAG_NO_FOLLOW_LAST_LINK) {
286 			strcpy(return_path, path);
287 		} else {
288 			realpath(path, return_path);
289 		}
290 
291 		return return_path;
292 	}
293 
294 	if (*path == '\0') {
295 		return NULL;
296 	}
297 
298 	bzero(tmp_path, sizeof(tmp_path));
299 	snprintf(tmp_path, sizeof(tmp_path), "%s", path);
300 
301 REDO:
302 	bzero((void *)&statbuf, sizeof(statbuf));
303 	bzero(return_path, sizeof(return_path));
304 	bzero(orig_pwd, sizeof(orig_pwd));
305 	bzero(token_path, sizeof(token_path));
306 
307 	getcwd(orig_pwd, sizeof(orig_pwd));
308 	// Check whether we are under valid path now
309 	if ((strcmp(orig_pwd, PATH_SMB_FTPD_ROOT) != 0) &&
310 		(smbftpd_is_under_valid_path(validshares, orig_pwd, 0) == 0)) {
311 		chdir(PATH_SMB_FTPD_ROOT);
312 		return NULL;
313 	}
314 
315 
316 	if (tmp_path[0] == '/') {
317 		snprintf(token_path, sizeof(token_path), "_@SMB_FTPD_ROOT@_%s", tmp_path);
318 		strcpy(return_path, "/");
319 	}else{
320 		snprintf(token_path, sizeof(token_path), "%s", tmp_path);
321 		snprintf(return_path, sizeof(return_path), "%s", tmp_path);
322 	}
323 
324 	// separate the path by '/', and chdir into the path to check
325 	// whether each component name is in the valid path
326 	for (token = strtok(token_path, "/"); token != NULL; token = strtok(NULL, "/")) {
327 		// we are under virtual root
328 		if (strcmp(token, "_@SMB_FTPD_ROOT@_") == 0) {
329 			snprintf(return_path, sizeof(return_path), "%s", PATH_SMB_FTPD_ROOT);
330 			chdir(PATH_SMB_FTPD_ROOT);
331 			continue;
332 		}
333 
334 		bzero(curdir, sizeof(curdir));
335 		getcwd(curdir, sizeof(curdir));
336 
337 		//  chdir to / when cd .. if we are under the root of share path
338 		if ( (strcmp(token, "..") == 0) && (is_share_root(validshares, curdir) == 1) ) {
339 			snprintf(return_path, sizeof(return_path), "%s", PATH_SMB_FTPD_ROOT);
340 			chdir(PATH_SMB_FTPD_ROOT);
341 			continue;
342 		}
343 
344 		// When under virtual root
345 		if (strcmp(curdir, PATH_SMB_FTPD_ROOT) == 0) {
346 			char *ptr;
347 
348 			// chdir to virtual root when cd .. or .
349 			if ((strcmp(token, ".") == 0) || (strcmp(token, "..") == 0)) {
350 				snprintf(return_path, sizeof(return_path), "%s", PATH_SMB_FTPD_ROOT);
351 				chdir(PATH_SMB_FTPD_ROOT);
352 				continue;
353 			// if next path is not valid share path, return NULL
354 			} else if ( (ptr = smbftpd_share_path_get(validshares, token)) == NULL) {
355 				chdir(orig_pwd);
356 				return NULL;
357 			} else {
358 				snprintf(return_path, sizeof(return_path), "%s", ptr);
359 				chdir(ptr);
360 				continue;
361 			}
362 		} else {
363 			// We are under valid share path now.
364 			if(smbftpd_is_under_valid_path(validshares, curdir, 0) == 0){
365 				// Not under valid share path
366 				chdir(orig_pwd);
367 				return NULL;
368 			}
369 
370 			err = lstat(token, (struct stat *) &statbuf);
371 			if (err != 0) {
372 				goto LAST_COMPOENT;
373 			}
374 
375 			// Dealing with directory
376 			if (S_ISDIR(statbuf.st_mode)){
377 				if (chdir(token)) {
378 					// Failed to chdir (directory not exist or permission denied)
379 					chdir(orig_pwd);
380 					return NULL;
381 				} else {
382 					bzero(curdir, sizeof(curdir));
383 					getcwd(curdir, sizeof(curdir));
384 					if (smbftpd_is_under_valid_path(validshares, curdir, 0) == 1) {
385 						strncpy(return_path, curdir, sizeof(return_path)-1);
386 						continue;
387 					} else {
388 						chdir(orig_pwd);
389 						return NULL;
390 					}
391 				}
392 			} else if ( S_ISLNK(statbuf.st_mode) &&
393 				    !(flags & FLAG_NO_FOLLOW_LINK) ) {  // Dealing with link
394 
395 				if (flags & FLAG_NO_FOLLOW_LAST_LINK) {
396 					if (*(token+strlen(token)+1) == '\0') { // Last component
397 						goto LAST_COMPOENT;
398 					}
399 				}
400 				err = readlink(token, tmp_path, sizeof(tmp_path) - 1);
401 				if (err < 0) {
402 					syslog(LOG_ERR, "%s (%d) Why I can lstat, but can't readlink? errno:%d(%s)",
403 						__FILE__, __LINE__, errno, strerror(errno));
404 					return NULL;
405 				} else {
406 					tmp_path[err] = '\0';
407 				}
408 
409 				if (*tmp_path != '/'){
410 					smbfptd_replace_share_path(validshares, curdir, sizeof(curdir));
411 					strcpy(return_path, curdir);
412 					snprintf(curdir, sizeof(curdir), "%s/%s", return_path, tmp_path);
413 
414 					// Re-compose the path and parse again.
415 					if ( *(token + strlen(token) + 1) == '\0') {
416 						snprintf(tmp_path, sizeof(tmp_path), "%s", curdir);
417 					} else {
418 						snprintf(tmp_path, sizeof(tmp_path), "%s/%s", curdir, token+strlen(token)+1);
419 					}
420 				} else {
421 					// Re-compose the path and parse again.
422 					if ( *(token + strlen(token) + 1) != '\0') {
423 						snprintf(tmp_path, sizeof(tmp_path), "%s/%s", curdir, token+strlen(token)+1);
424 					}
425 				}
426 
427 				chdir(orig_pwd);
428 				goto REDO;
429 
430 			} else {	// Dealing with file
431 	LAST_COMPOENT:
432 				if ((flags & FLAG_CHECK_WRITABLE) &&
433 					smbftpd_is_under_valid_path(validshares, curdir, 1) == 0) {
434 					chdir(orig_pwd);
435 					return NULL;
436 				} else {
437 					snprintf(return_path, sizeof(return_path), "%s/%s",
438 							 curdir, token);
439 
440 					// There should be no next dir since it's a file
441 					if (strtok(NULL, "/")) {
442 						chdir(orig_pwd);
443 						return NULL;
444 					}
445 					chdir(orig_pwd);
446 					return return_path;
447 				}
448 			} // end of file or directory
449 		} // end of if under virtual root
450 	} // end of for
451 	if (flags & FLAG_CHECK_WRITABLE) {
452 		getcwd(curdir, sizeof(curdir));
453 		if (is_share_root(validshares, curdir) ||
454 			(smbftpd_is_under_valid_path(validshares, curdir, 1) == 0)) {
455 			chdir(orig_pwd);
456 			return NULL;
457 		}else{
458 			chdir(orig_pwd);
459 			return return_path;
460 		}
461 	} else {
462 		chdir(orig_pwd);
463 		return return_path;
464 	}
465 }
466 
467 /**
468  * Free the smbftpd_valid_share_t linked list.
469  *
470  * @param validshares
471  */
smbftpd_valid_share_free(smbftpd_valid_share_t ** validshares)472 void smbftpd_valid_share_free(smbftpd_valid_share_t **validshares)
473 {
474 	smbftpd_valid_share_t *p;
475 
476 	while (*validshares) {
477 		p = *validshares;
478 		*validshares = p->next;
479 		if (p->share) {
480 			free(p->share);
481 		}
482 		if (p->path) {
483 			free(p->path);
484 		}
485 		free(p);
486 	}
487 
488 	return;
489 }
490 
491 /* Get shares that is accessable by szUser and put in smbftpd_share_t
492  *
493  * Return Values:
494  *	0: Success
495  *	-1: Failed
496  */
smbftpd_valid_share_get(const char * user,const char * home_dir,smbftpd_share_t * shares,smbftpd_valid_share_t ** ppvalid_shares)497 int smbftpd_valid_share_get(const char *user, const char *home_dir,
498 							smbftpd_share_t *shares, smbftpd_valid_share_t **ppvalid_shares)
499 {
500 	smbftpd_share_t *curr;
501 	struct stat statcheck;
502 	char *home = NULL;
503 	int writable = 0;
504 	int err = -1;
505 	int deny;
506 
507 	if (NULL == user) {
508 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
509 		return -1;
510 	}
511 
512 	if (NULL == shares) {
513 		*ppvalid_shares = NULL;
514 		return 0;
515 	}
516 
517 	curr = shares;
518 	while (curr) {
519 		home = NULL;
520 		deny = 1;
521 		writable = 0;
522 
523 		if (strcmp(curr->share, "homes") == 0) {
524 			char *ptr = real_full_path(home_dir);
525 			if (!ptr) {
526 				goto SKIP;
527 			}
528 			home = strdup(ptr);
529 			if (!home) {
530 				syslog(LOG_ERR, "%s (%d) Failed to strdup(%s). (%s)",
531 					   __FILE__, __LINE__, smbftpd_session.home, strerror(errno));
532 				goto Error;
533 			}
534 			deny = 0;
535 			writable = 1;
536 			goto SKIP;
537 		}
538 
539 		if (1 == is_user_in_list(user, curr->rw)) {
540 			writable = 1;
541 			deny = 0;
542 		}
543 
544 		if (deny == 1) {
545 			if (1 == is_user_in_list(user, curr->ro)) {
546 				deny = 0;
547 			}
548 		}
549 SKIP:
550 		// Shared is accessable for szUser
551 		if (deny == 0) {
552 			if (home || ((!stat(curr->path, &statcheck)) &&
553 				(S_ISDIR(statcheck.st_mode)))) {
554 				smbftpd_valid_share_t *pSet;
555 
556 				pSet = calloc(1, sizeof(smbftpd_valid_share_t));
557 				if (pSet == NULL) {
558 					syslog(LOG_ERR, "%s (%d) Ran out of memory.", __FILE__, __LINE__);
559 					if (home) {
560 						free(home);
561 					}
562 					goto Error;
563 				}
564 				pSet->share = home?strdup("home"):strdup(curr->share);
565 				pSet->path = home?home:strdup(curr->path);
566 				pSet->browseable = curr->browseable;
567 
568 				if (writable) {
569 					pSet->writable = 1;
570 				}
571 				if (is_user_in_list(user, curr->disable_download)) {
572 					pSet->disable_download = 1;
573 				}
574 				if (is_user_in_list(user, curr->disable_ls)) {
575 					pSet->disable_ls = 1;
576 				}
577 				if (is_user_in_list(user, curr->disable_modify)) {
578 					pSet->disable_modify = 1;
579 				}
580 				pSet->next = *ppvalid_shares;
581 				*ppvalid_shares = pSet;
582 
583 			}
584 		}
585 		curr = curr->next;
586 	}
587 
588 	err = 0;
589 Error:
590 	if (err) {
591 		smbftpd_valid_share_free(ppvalid_shares);
592 	}
593 	return err;
594 }
595 
596 /**
597  * Free all smbftpd_share linked list.
598  *
599  * Make sure you set the smb_shares->next to NULL if you need to
600  * free only one share.
601  *
602  * @param smb_shares The linked list of smbftpd_share_t.
603  */
smbftpd_share_free(smbftpd_share_t ** smb_shares)604 void smbftpd_share_free(smbftpd_share_t **smb_shares)
605 {
606 	smbftpd_share_t *p;
607 
608 	while (*smb_shares) {
609 		p = *smb_shares;
610 		*smb_shares = p->next;
611 
612 		if (p->share) {
613 			free(p->share);
614 		}
615 		if (p->path) {
616 			free(p->path);
617 		}
618 		if (p->rw) {
619 			free(p->rw);
620 		}
621 		if (p->ro) {
622 			free(p->ro);
623 		}
624 		if (p->disable_download) {
625 			free(p->disable_download);
626 		}
627 		if (p->disable_ls) {
628 			free(p->disable_ls);
629 		}
630 		free(p);
631 	}
632 }
633 
634 /**
635  * This function will read the configuration in given smbftpd_share.conf
636  *
637  * It will allocate memory for smb_shares to store share
638  * information. So smbftpd_share_free() must be call to free it.
639  *
640  * @param path
641  * @param smb_shares
642  *
643  * @return 0: Success
644  *         -1: Failed
645  */
smbftpd_share_enum(char * path,smbftpd_share_t ** smb_shares)646 int smbftpd_share_enum(char *path, smbftpd_share_t **smb_shares)
647 {
648 	smbftpd_share_t *prev, *cur;
649 	FILE *fp = NULL;
650 	char line[LINE_MAX], *ptr1, *ptr2;
651 	int err = -1;
652 
653 	if (NULL == path) {
654 		syslog(LOG_ERR, "%s (%d) bad parameter", __FILE__, __LINE__);
655 		return -1;
656 	}
657 
658 	fp = fopen(path, "r");
659 	if (NULL == fp) {
660 		syslog(LOG_ERR, "%s (%d) Failed to open share config [%s], errno:%d (%s)",
661 			   __FILE__, __LINE__, path, errno, strerror(errno));
662 		goto Error;
663 	}
664 
665 	while (NULL != fgets(line, sizeof(line), fp)) {
666 		// Skip spaces
667 		ptr1 = str_trim_space(line);
668 
669 		if ((*ptr1 == '#') || (*ptr1 == '\0') || (*ptr1 == '\n')){
670 			continue;
671 		} else if (*ptr1 == '[') {
672 			ptr1++;
673 			ptr2 = strchr(ptr1, ']');
674 			if ((ptr2 != NULL) && (ptr2 - ptr1 > 0)) {
675 				smbftpd_share_t *p;
676 
677 				*ptr2 = '\0';
678 				str_trim_space(ptr1);
679 
680 				p = calloc(1, sizeof(smbftpd_share_t));
681 				if (p == NULL) {
682 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
683 					goto Error;
684 				}
685 				p->share = strdup(ptr1);
686 				if (!p->share) {
687 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
688 					free(p);
689 					goto Error;
690 				}
691 				p->browseable = 1;
692 				p->next = *smb_shares;
693 				*smb_shares = p;
694 			}
695 		} else {
696 			if (!*smb_shares) {
697 				/* We have not gotten a section */
698 				continue;
699 			}
700 			ptr2 = strchr(ptr1, '=');
701 			if (NULL == ptr2) {
702 				continue;
703 			}
704 			*ptr2 = '\0';
705 			ptr2++;
706 			str_trim_space_quote(ptr1);
707 			str_trim_space_quote(ptr2);
708 			if (strcmp(ptr1, "rw") == 0) {
709 				(*smb_shares)->rw = strdup(ptr2);
710 				if (!(*smb_shares)->rw) {
711 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
712 					goto Error;
713 				}
714 			} else if (strcmp(ptr1, "ro") == 0) {
715 				(*smb_shares)->ro = strdup(ptr2);
716 				if (!(*smb_shares)->ro) {
717 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
718 					goto Error;
719 				}
720 			} else if (strcmp(ptr1, "disable_download") == 0) {
721 				(*smb_shares)->disable_download = strdup(ptr2);
722 				if (!(*smb_shares)->disable_download) {
723 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
724 					goto Error;
725 				}
726 			} else if (strcmp(ptr1, "disable_ls") == 0) {
727 				(*smb_shares)->disable_ls = strdup(ptr2);
728 				if (!(*smb_shares)->disable_ls) {
729 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
730 					goto Error;
731 				}
732 			} else if (strcmp(ptr1, "disable_modify") == 0) {
733 				(*smb_shares)->disable_modify = strdup(ptr2);
734 				if (!(*smb_shares)->disable_modify) {
735 					syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
736 					goto Error;
737 				}
738 			} else if (strcmp(ptr1, "path") == 0) {
739 
740 				char *ptr = real_full_path(ptr2);
741 				if (!ptr) {
742 					syslog(LOG_ERR, "%s (%d) path \"%s\" does not exist.",
743 						   __FILE__, __LINE__, ptr2);
744 				} else {
745 					(*smb_shares)->path = strdup(ptr);
746 					if (!(*smb_shares)->path) {
747 						syslog(LOG_ERR, "%s (%d) Out of memory", __FILE__, __LINE__);
748 						goto Error;
749 					}
750 				}
751 			} else if (strcmp(ptr1, "browseable") == 0) {
752 				if (strcasecmp(ptr2, "no") == 0) {
753 					(*smb_shares)->browseable = 0;
754 				}
755 			}
756 		}
757 	}
758 
759 	// Remove shares that have no path beside homes
760 	prev = cur = *smb_shares;
761 	while (cur) {
762 		if ((strcmp(cur->share, "homes") != 0) && !cur->path) {
763 			if (prev == cur) { /* Head */
764 				*smb_shares = cur->next;
765 				cur->next = NULL;
766 				smbftpd_share_free(&cur);
767 				prev = cur = *smb_shares;
768 			} else {
769 				prev->next = cur->next;
770 				cur->next = NULL;
771 				smbftpd_share_free(&cur);
772 				cur = prev->next;
773 			}
774 		} else {
775 			prev = cur;
776 			cur = cur->next;
777 		}
778 	}
779 
780 	err = 0;
781 Error:
782 	if (fp) {
783 		fclose(fp);
784 	}
785 	if (err) {
786 		if (*smb_shares != NULL) {
787 			smbftpd_share_free(smb_shares);
788 		}
789 		return -1;
790 	} else {
791 		return 0;
792 	}
793 }
794 
795