1 /* Copyright 2003-2008 Wang, Chun-Pin All rights reserved. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <strings.h>
5 #include <unistd.h>
6 #include <sys/param.h>
7 #include <syslog.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <grp.h>
11 #include <pwd.h>
12 #include <ctype.h>
13 
14 #include "smbftpd.h"
15 #include "auth.h"
16 
17 #ifndef LINE_MAX
18 	#define LINE_MAX 2048
19 #endif
20 
set_get_value(struct opt_set * set,const char * user)21 const char *set_get_value(struct opt_set *set, const char *user)
22 {
23 	struct opt_set *p = set;
24 	char *key;
25 	int match = 0;
26 
27 	if (!user || !set) {
28 		return NULL;
29 	}
30 
31 	while (p) {
32 		key = p->key;
33 		if (*key != '@') {       //user
34 			if (0 == strcmp(key, user)) {
35 				match = 1;
36 			}
37 		} else if (*(key+1) == '\0') { /* "@" match all */
38 			match = 1;
39 		} else {        /* Group */
40 			if (is_user_in_group(user, key + 1)) {
41 				match = 1;
42 			}
43 		}
44 		if (match) {
45 			return p->value;
46 		}
47 		p = p->next;
48 	}
49 	return NULL;
50 }
51 /**
52  * Allocate another string and copy string "s" into new buffer.
53  * When there is double quote in s, put add another double.
54  *
55  * For example,
56  *     This is "s"
57  * will become
58  *     This is ""s""
59  *
60  * @param s
61  *
62  * @return Success: Returen a new string, call should free the string
63  *         Malloc failed: Return NULL
64  */
doublequote(const char * s)65 char *doublequote(const char *s)
66 {
67 	int n;
68 	const char *p1;
69 	char *p, *s2;
70 
71 	for (p1 = s, n = 0; *p1; p1++)
72 		if (*p1 == '"')
73 			n++;
74 
75 	if ((s2 = malloc(p1 - s + n + 1)) == NULL)
76 		return(NULL);
77 
78 	for (p = s2; *s; s++, p++) {
79 		if ((*p = *s) == '"')
80 			*(++p) = '"';
81 	}
82 	*p = '\0';
83 
84 	return(s2);
85 }
86 
87 /**
88  * This function will remove the spaces ' ' and tab '\t' and
89  * newline characters '\r', '\n' in the front and tail of the
90  * given "str". So the "str" buffer will be modified.
91  *
92  * if remove_quote is 1, we will also remove the in pairs double
93  * quote '"' or single quote '\''.
94  *
95  * For example, if the string is "   this is a string  ", it
96  * will become "this is a string".
97  *
98  * If the string is " \"this is a string\" ", when remove_quote
99  * is 1, it will become "this is a string".
100  *
101  * @param str
102  * @param remove_quote
103  *
104  * @return The pointer of str
105  */
str_trim(char * str,int remove_quote)106 static char *str_trim(char *str, int remove_quote)
107 {
108 	char *head, *tail;
109 
110 	if (!str) {
111 		return NULL;
112 	}
113 
114 	for (head = str; isspace(*head) && *head != 0; head++);
115 	for (tail = head + strlen(head) - 1; tail>head && isspace(*tail); tail--);
116 
117 	*(tail+1) = 0;
118 
119 	if (remove_quote) {
120 		if ((tail > head) && ((*head == '"' && *tail == '"') ||
121 							  (*head == '\'' && *tail == '\''))) {
122 			head++;
123 			*tail = 0;
124 			tail--;
125 		}
126 	}
127 
128 	if (str != head) {
129 		memmove(str, head, (tail - head + 2));
130 	}
131 
132 	return str;
133 }
134 
str_trim_space(char * str)135 char *str_trim_space(char *str)
136 {
137 	return str_trim(str, 0);
138 }
139 
str_trim_space_quote(char * str)140 char *str_trim_space_quote(char *str)
141 {
142 	return str_trim(str, 1);
143 }
144 
145 
146 /**
147  * Check whether user is in list. The list is a string of
148  * users/groups that separate by ','. The group name has
149  * prefix @.
150  *
151  * For example szList = "user1, user2, user3, @group1, @group2..."
152  *
153  * We will get each user in the list and check when the user name
154  * is the same. When we get group, we will check whether user
155  * belongs to the group.
156  *
157  * @param user   User name to check
158  * @param list   The string that contains users and groups separated by ",".
159  *
160  * @return 1: Yes, the user is in the list
161  *         0: No, the user is not in the list
162  */
is_user_in_list(const char * user,const char * list)163 int is_user_in_list(const char *user, const char *list)
164 {
165 	char *tmplist = NULL, *token;
166 	int err = 0;
167 
168 	if (!user || !list) {
169 		return err;
170 	}
171 
172 	tmplist = (char *)malloc(strlen(list) + 1);
173 	if (tmplist == NULL) {
174 		syslog(LOG_ERR, "%s (%d) failed to allocate memory, errno:%d(%s)",
175 			   __FILE__, __LINE__, errno, strerror(errno));
176 		return err;
177 	}
178 	strcpy(tmplist, list);
179 
180 	for (token = strtok(tmplist, ","); token; token = strtok(NULL, ",")) {
181 		str_trim_space(token);
182 		if (*token != '@') {	   //user
183 			if (0 == strcmp(user, token)) {
184 				err = 1;
185 				break;
186 			}
187 		} else {
188 			if (1 == smbftpd_auth_is_user_in_group(user, token+1)) {
189 				err = 1;
190 				goto Error;
191 			}
192 		}
193 	}
194 
195 Error:
196 	if (tmplist != NULL) {
197 		free(tmplist);
198 	}
199 	return err;
200 }
201 
202 /**
203  * Check whether user belongs to group. We will check group id and
204  * its members.
205  *
206  * @param user   The user name to check
207  * @param group  The group name to search
208  *
209  * @return 1: Yes, the user belongs to the group.
210  *         0: No, not belongs to the group.
211  */
is_user_in_group(const char * user,const char * group)212 int is_user_in_group(const char *user, const char *group)
213 {
214 	return smbftpd_auth_is_user_in_group(user, group);
215 }
216 
217 /**
218  * Config file parser function. This function is used to parse config
219  * file that is the following format:
220  *
221  *    Option1   Value
222  *    Option2   Value
223  *
224  * We will read each line of config file and pass the option/value to
225  * the opt_handler function.
226  *
227  * The opt_handler function will then assign/convert the value to
228  * proper format.
229  *
230  * @param file   The path of the config file.
231  * @param opt_handler
232  *               The option/value handler function.
233  *
234  * @return 0: Success
235  *         -1: Failed
236  */
smbftpd_config_parser(const char * file,int (* opt_handler)(char * option,char * opt_arg))237 int smbftpd_config_parser(const char *file, int (*opt_handler)(char *option, char *opt_arg))
238 {
239 	FILE *pf = NULL;
240 	char line[LINE_MAX];
241 	char *option, *opt_arg;
242 	int error = -1, len = 0;
243 
244 	if (NULL == file || !opt_handler) {
245 		return -1;
246 	}
247 
248 	pf = fopen(file, "r");
249 	if (!pf) {
250 		syslog(LOG_ERR, "%s (%d) Failed to open [%s] (%s)", __FILE__, __LINE__,
251 			   file, strerror(errno));
252 		return -1;
253 	}
254 
255 	while (NULL != (fgets(line, sizeof(line), pf))) {
256 		len = strlen(line);
257 
258 		if (line[len - 1] == '\n') {
259 			line[len - 1] = '\0';
260 		}
261 		option = line;
262 		// Trim space
263 		while ((*option == ' ') || (*option == '\t')) {
264 			option++;
265 		}
266 		if (*option == '\0' || *option == '#') {
267 			continue;
268 		}
269 
270 		opt_arg = option;
271 		while ((*opt_arg != ' ') && (*opt_arg != '\t') && (*opt_arg != '\0')) {
272 			opt_arg++;
273 		}
274 		if (opt_arg == option) {
275 			// Empty line
276 			continue;
277 		}
278 
279 		*opt_arg = '\0';
280 		opt_arg++;
281 		str_trim_space_quote(opt_arg);
282 		if (*opt_arg == '\0') {
283 			syslog(LOG_ERR, "%s (%d) bad syntax of config option %s",
284 				   __FILE__, __LINE__, option);
285 			goto Error;
286 		}
287 
288 		if (0 != opt_handler(option, opt_arg)) {
289 			goto Error;
290 		}
291 	}
292 
293 	error = 0;
294 Error:
295 	if (pf) {
296 		fclose(pf);
297 	}
298 
299 	return error;
300 }
301