1 /* vi:ai:et:ts=8 sw=2
2 */
3 /*
4 * wzdftpd - a modular and cool ftp server
5 * Copyright (C) 2002-2004 Pierre Chifflier
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * As a special exemption, Pierre Chifflier
22 * and other respective copyright holders give permission to link this program
23 * with OpenSSL, and distribute the resulting executable, without including
24 * the source code for OpenSSL in the source distribution.
25 */
26
27 #include "wzd_all.h"
28
29 #ifndef WZD_USE_PCH
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36
37 #include <sys/types.h>
38
39 #ifdef WIN32
40 #include <winsock2.h>
41 #include <direct.h> /* _getcwd */
42 #else
43 #include <unistd.h>
44
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #endif
49
50 #include "wzd_structs.h"
51
52 #include "wzd_fs.h"
53 #include "wzd_group.h"
54 #include "wzd_libmain.h"
55 #include "wzd_log.h"
56 #include "wzd_misc.h"
57 #include "wzd_user.h"
58
59 #include "wzd_debug.h"
60
61 #endif /* WZD_USE_PCH */
62
63 static gid_t _max_gid = 0;
64 static wzd_group_t ** _group_array = NULL;
65
66 /** \brief Allocate a new empty structure for a group
67 */
group_allocate(void)68 wzd_group_t * group_allocate(void)
69 {
70 wzd_group_t * group;
71
72 group = wzd_malloc(sizeof(wzd_group_t));
73
74 WZD_ASSERT_RETURN(group != NULL, NULL);
75 if (group == NULL) {
76 out_log(LEVEL_CRITICAL,"FATAL group_allocate out of memory\n");
77 return NULL;
78 }
79
80 group_init_struct(group);
81
82 return group;
83 }
84
85 /** \brief Initialize members of struct \a group
86 */
group_init_struct(wzd_group_t * group)87 void group_init_struct(wzd_group_t * group)
88 {
89 WZD_ASSERT_VOID(group != NULL);
90 if (group == NULL) return;
91
92 memset(group,0,sizeof(wzd_group_t));
93
94 group->gid = (gid_t)-1;
95 group->groupperms = 0xffffffff;
96 }
97
98 /** \brief Free memory used by a \a group structure
99 */
group_free(wzd_group_t * group)100 void group_free(wzd_group_t * group)
101 {
102 if (group == NULL) return;
103
104 ip_list_free(group->ip_list);
105 wzd_free(group);
106 }
107
108 /** \brief Create a new group, giving default parameters
109 * \return The new group, or NULL. If \a err is provided, set it to
110 * the error code.
111 */
group_create(const char * groupname,wzd_context_t * context,wzd_config_t * config,int * err)112 wzd_group_t * group_create(const char * groupname, wzd_context_t * context, wzd_config_t * config, int * err)
113 {
114 wzd_group_t * newgroup;
115 const char * homedir;
116
117 WZD_ASSERT_RETURN( groupname != NULL, NULL );
118 if (groupname == NULL) {
119 if (err) *err = E_PARAM_NULL;
120 return NULL;
121 }
122
123 if (strlen(groupname) == 0 || strlen(groupname) >= HARD_GROUPNAME_LENGTH) {
124 if (err) *err = E_PARAM_BIG;
125 return NULL;
126 }
127
128 if (GetGroupByName(groupname) != NULL) {
129 if (err) *err = E_PARAM_EXIST;
130 return NULL;
131 }
132
133 /* homedir */
134 if (context != NULL) {
135 wzd_user_t * me;
136
137 me = GetUserByID(context->userid);
138 if (me != NULL && me->group_num > 0) {
139 wzd_group_t * mygroup = GetGroupByID(me->groups[0]);
140 homedir = mygroup->defaultpath;
141 } else {
142 homedir = me->rootpath;
143 }
144
145 /* check if homedir exist */
146 {
147 fs_filestat_t s;
148 if (fs_file_stat(homedir,&s) || !S_ISDIR(s.mode)) {
149 out_log(LEVEL_HIGH,"WARNING homedir %s does not exist (while creating group %s)\n",homedir,groupname);
150 }
151 }
152 } else {
153 out_log(LEVEL_HIGH,"WARNING could not get a default homedir for new group %s\n",groupname);
154 /** \todo use a config parameter or a default group to get the
155 * default path ?
156 */
157 homedir = "";
158 }
159
160 if (strlen(homedir) >= WZD_MAX_PATH) {
161 out_log(LEVEL_HIGH,"ERROR homedir is too long (>= %d chars) while creating group %s\n",WZD_MAX_PATH,groupname);
162 if (err) *err = E_PARAM_BIG;
163 return NULL;
164 }
165
166 /* finally, create the new group */
167 newgroup = group_allocate();
168 strncpy(newgroup->groupname,groupname,HARD_GROUPNAME_LENGTH);
169 strncpy(newgroup->defaultpath,homedir,WZD_MAX_PATH);
170
171 return newgroup;
172 }
173
174 /** \brief Register a group to the main server
175 * \return The gid of the registered group, or -1 on error
176 */
group_register(wzd_group_t * group,u16_t backend_id)177 gid_t group_register(wzd_group_t * group, u16_t backend_id)
178 {
179 gid_t gid;
180
181 WZD_ASSERT(group != NULL);
182 if (group == NULL) return (gid_t)-1;
183
184 WZD_ASSERT(group->gid != (gid_t)-1);
185 if (group->gid == (gid_t)-1) return (gid_t)-1;
186
187 /* safety check */
188 if (group->gid >= INT_MAX) {
189 out_log(LEVEL_HIGH, "ERROR group_register(gid=%d): gid too big\n",group->gid);
190 return (gid_t)-1;
191 }
192
193 WZD_MUTEX_LOCK(SET_MUTEX_USER);
194
195 gid = group->gid;
196
197 if (gid >= _max_gid) {
198 size_t size; /* size of extent */
199
200 if (gid >= _max_gid + 255)
201 size = gid - _max_gid;
202 else
203 size = 256;
204 _group_array = wzd_realloc(_group_array, (_max_gid + size + 1)*sizeof(wzd_group_t*));
205 memset(_group_array + _max_gid, 0, (size+1) * sizeof(wzd_group_t*));
206 _max_gid = _max_gid + size;
207 }
208
209 if (_group_array[gid] != NULL) {
210 out_log(LEVEL_NORMAL, "INFO group_register(gid=%d): another group is already present (%s)\n",gid,_group_array[gid]->groupname);
211 WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
212 return -1;
213 }
214
215 _group_array[gid] = group;
216 group->backend_id = backend_id;
217
218 out_log(LEVEL_FLOOD,"DEBUG registered gid %d with backend %d\n",gid,backend_id);
219
220 WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
221 return gid;
222 }
223
224 /** \brief Update a registered group atomically. Datas are copied,
225 * and old group is freed.
226 * A pointer to the old group is still valid (change is done in-place)
227 * If the gid had changed, the group will be moved
228 * \return 0 if ok
229 */
group_update(gid_t gid,wzd_group_t * new_group)230 int group_update(gid_t gid, wzd_group_t * new_group)
231 {
232 wzd_group_t * buffer;
233
234 if (gid == (gid_t)-1) return -1;
235 if (gid > _max_gid) return -1;
236 if (_group_array[gid] == NULL) return -2;
237
238 if (gid != new_group->gid) {
239 if (_group_array[new_group->gid] != NULL) return -3;
240 }
241
242 /* same group ? do nothing */
243 if (gid == new_group->gid && _group_array[gid] == new_group) return 0;
244
245 WZD_MUTEX_LOCK(SET_MUTEX_USER);
246 /* backup old group */
247 buffer = wzd_malloc(sizeof(wzd_group_t));
248 *buffer = *_group_array[gid];
249 /* update group */
250 *_group_array[gid] = *new_group;
251 group_free(buffer);
252 if (gid != new_group->gid) {
253 _group_array[new_group->gid] = _group_array[gid];
254 _group_array[gid] = NULL;
255 }
256 WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
257
258 return 0;
259 }
260
261 /** \brief Unregister a group to the main server
262 * The \a group struct must be freed using group_free()
263 * \return The unregistered group structure, or NULL on error
264 */
group_unregister(gid_t gid)265 wzd_group_t * group_unregister(gid_t gid)
266 {
267 wzd_group_t * group = NULL;
268
269 WZD_ASSERT_RETURN(gid != (gid_t)-1, NULL);
270 if (gid == (gid_t)-1) return NULL;
271
272 if (gid > _max_gid) return NULL;
273
274 WZD_MUTEX_LOCK(SET_MUTEX_USER);
275
276 if (_group_array[gid] != NULL) {
277 group = _group_array[gid];
278 _group_array[gid] = NULL;
279 }
280
281 WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
282 out_log(LEVEL_FLOOD,"DEBUG unregistered gid %d\n",gid);
283
284 return group;
285 }
286
287 /** \brief Free memory used to register groups
288 * \warning Also free ALL registered groups !
289 */
group_free_registry(void)290 void group_free_registry(void)
291 {
292 gid_t gid;
293 WZD_MUTEX_LOCK(SET_MUTEX_USER);
294 if (_group_array != NULL) {
295 for (gid=0; gid<=_max_gid; gid++) {
296 group_free(_group_array[gid]);
297 }
298 }
299 wzd_free(_group_array);
300 _group_array = NULL;
301 _max_gid = 0;
302 WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
303 }
304
305 /** \brief Get registered group using the \a gid
306 * \return The group, or NULL
307 */
group_get_by_id(gid_t gid)308 wzd_group_t * group_get_by_id(gid_t gid)
309 {
310 if (gid == (gid_t)-1) return NULL;
311 if (gid > _max_gid) return NULL;
312 if (_max_gid == 0) return NULL;
313
314 return _group_array[gid];
315 }
316
317 /** \brief Get registered group using the \a name
318 * \return The group, or NULL
319 * \todo Re-implement the function using a hash table
320 */
group_get_by_name(const char * groupname)321 wzd_group_t * group_get_by_name(const char * groupname)
322 {
323 gid_t gid;
324
325 if (groupname == NULL || strlen(groupname)<1 || _max_gid==0) return NULL;
326
327 /* We don't need to lock the access since the _group_array can only grow */
328 for (gid=0; gid<=_max_gid; gid++) {
329 if (_group_array[gid] != NULL
330 && _group_array[gid]->groupname != NULL
331 && strcmp(groupname,_group_array[gid]->groupname)==0)
332 return _group_array[gid];
333 }
334 return NULL;
335 }
336
337 /** \brief Get list or groups register for a specific backend
338 * The returned list is terminated by -1, and must be freed with wzd_free()
339 */
group_get_list(u16_t backend_id)340 gid_t * group_get_list(u16_t backend_id)
341 {
342 gid_t * gid_list = NULL;
343 gid_t size;
344 int index;
345 gid_t gid;
346
347 /** \todo XXX we should use locks (and be careful to avoid deadlocks) */
348
349 /** \todo it would be better to get the real number of used gid */
350 size = _max_gid;
351
352 gid_list = (gid_t*)wzd_malloc((size+1)*sizeof(gid_t));
353 index = 0;
354 /* We don't need to lock the access since the _group_array can only grow */
355 for (gid=0; gid<size; gid++) {
356 if (_group_array[gid] != NULL
357 && _group_array[gid]->gid != INVALID_USER)
358 gid_list[index++] = _group_array[gid]->gid;
359 }
360 gid_list[index] = (gid_t)-1;
361 gid_list[size] = (gid_t)-1;
362
363 return gid_list;
364 }
365
366 /** \brief Find the first free gid, starting from \a start
367 */
group_find_free_gid(gid_t start)368 gid_t group_find_free_gid(gid_t start)
369 {
370 gid_t gid;
371
372 if (start == (gid_t)-1) start = 0;
373
374 /** \todo locking may be harmful if this function is called from another
375 * group_x() function
376 */
377 /* WZD_MUTEX_LOCK(SET_MUTEX_USER);*/
378 for (gid = start; gid < _max_gid && gid != (gid_t)-1; gid++) {
379 if (_group_array[gid] == NULL) break;
380 }
381 /* WZD_MUTEX_UNLOCK(SET_MUTEX_USER);*/
382
383 return gid;
384 }
385
386 /** \brief Add an ip to the list of authorized/forbidden ips
387 * \return 0 if ok
388 */
group_ip_add(wzd_group_t * group,const char * ip,int is_authorized)389 int group_ip_add(wzd_group_t * group, const char * ip, int is_authorized)
390 {
391 WZD_ASSERT( group != NULL );
392 if (group == NULL) return -1;
393
394 /** \note The number of stored ips per group is no more limited */
395
396 return ip_add_check(&group->ip_list, ip, is_authorized);
397 }
398
399