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_ip.h"
55 #include "wzd_libmain.h"
56 #include "wzd_log.h"
57 #include "wzd_misc.h"
58 #include "wzd_user.h"
59 
60 #include "wzd_debug.h"
61 
62 #endif /* WZD_USE_PCH */
63 
64 static uid_t _max_uid = 0;
65 static wzd_user_t ** _user_array = NULL;
66 
67 
68 /** \brief Allocate a new empty structure for a user
69  */
user_allocate(void)70 wzd_user_t * user_allocate(void)
71 {
72   wzd_user_t * user;
73 
74   user = wzd_malloc(sizeof(wzd_user_t));
75 
76   WZD_ASSERT_RETURN(user != NULL, NULL);
77   if (user == NULL) {
78     out_log(LEVEL_CRITICAL,"FATAL user_allocate out of memory\n");
79     return NULL;
80   }
81 
82   user_init_struct(user);
83 
84   return user;
85 }
86 
87 /** \brief Initialize members of struct \a user
88  */
user_init_struct(wzd_user_t * user)89 void user_init_struct(wzd_user_t * user)
90 {
91   WZD_ASSERT_VOID(user != NULL);
92   if (user == NULL) return;
93 
94   memset(user,0,sizeof(wzd_user_t));
95 
96   user->uid = (uid_t)-1;
97 }
98 
99 /** \brief Free memory used by a \a user structure
100  */
user_free(wzd_user_t * user)101 void user_free(wzd_user_t * user)
102 {
103   if (user == NULL) return;
104 
105   ip_list_free(user->ip_list);
106   wzd_free(user);
107 }
108 
109 /** \brief Create a new user, giving default parameters
110  * \return The new user, or NULL. If \a err is provided, set it to
111  * the error code.
112  */
user_create(const char * username,const char * pass,const char * groupname,wzd_context_t * context,wzd_config_t * config,int * err)113 wzd_user_t * user_create(const char * username, const char * pass, const char * groupname, wzd_context_t * context, wzd_config_t * config, int * err)
114 {
115   wzd_user_t * newuser;
116   wzd_group_t * group = NULL;
117   const char * homedir;
118   unsigned int ratio;
119 
120   WZD_ASSERT_RETURN( username != NULL, NULL );
121   if (username == NULL) {
122     if (err) *err = E_PARAM_NULL;
123     return NULL;
124   }
125 
126   if (strlen(username) == 0 || strlen(username) >= HARD_USERNAME_LENGTH) {
127     if (err) *err = E_PARAM_BIG;
128     return NULL;
129   }
130 
131   if (GetUserByName(username) != NULL) {
132     if (err) *err = E_PARAM_EXIST;
133     return NULL;
134   }
135 
136   if (groupname == NULL) {
137     if (err) *err = E_PARAM_NULL;
138     return NULL;
139   }
140 
141   group = GetGroupByName(groupname);
142 
143   if (group == NULL) {
144     if (err) *err = E_PARAM_INVALID;
145     return NULL;
146   }
147 
148   /* create new user */
149   newuser = user_allocate();
150 
151   homedir = group->defaultpath;
152   ratio = group->ratio;
153   newuser->userperms = group->groupperms;
154 
155   /* check if homedir exist */
156   {
157     fs_filestat_t s;
158     if (fs_file_stat(homedir,&s) || !S_ISDIR(s.mode)) {
159       out_log(LEVEL_HIGH,"WARNING homedir %s does not exist (while creating user %s)\n",homedir,username);
160     }
161   }
162 
163   strncpy(newuser->username, username, HARD_USERNAME_LENGTH-1);
164   strncpy(newuser->userpass, pass, MAX_PASS_LENGTH-1);
165   strncpy(newuser->rootpath, homedir, WZD_MAX_PATH-1);
166   newuser->group_num=0;
167   if (group != NULL) {
168     newuser->groups[0] = group->gid;
169     if (newuser->groups[0] != INVALID_USER) newuser->group_num = 1;
170   }
171 
172   return newuser;
173 }
174 
175 /** \brief Register a user to the main server
176  * \return The uid of the registered user, or -1 on error
177  */
user_register(wzd_user_t * user,u16_t backend_id)178 uid_t user_register(wzd_user_t * user, u16_t backend_id)
179 {
180   uid_t uid;
181 
182   WZD_ASSERT(user != NULL);
183   if (user == NULL) return (uid_t)-1;
184 
185   WZD_ASSERT(user->uid != (uid_t)-1);
186   if (user->uid == (uid_t)-1) return (uid_t)-1;
187 
188   /* safety check */
189   if (user->uid >= INT_MAX) {
190     out_log(LEVEL_HIGH, "ERROR user_register(uid=%d): uid too big\n",user->uid);
191     return (uid_t)-1;
192   }
193 
194   WZD_MUTEX_LOCK(SET_MUTEX_USER);
195 
196   /** \todo we should check unicity of username */
197 
198   uid = user->uid;
199 
200   if (uid >= _max_uid) {
201     size_t size; /* size of extent */
202 
203     if (uid >= _max_uid + 255)
204       size = uid - _max_uid;
205     else
206       size = 256;
207     _user_array = wzd_realloc(_user_array, (_max_uid + size + 1)*sizeof(wzd_user_t*));
208     memset(_user_array + _max_uid, 0, (size+1) * sizeof(wzd_user_t*));
209     _max_uid = _max_uid + size;
210   }
211 
212   if (_user_array[uid] != NULL) {
213     out_log(LEVEL_NORMAL, "INFO user_register(uid=%d): another user is already present (%s)\n",uid,_user_array[uid]->username);
214     WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
215     return -1;
216   }
217 
218   _user_array[uid] = user;
219   user->backend_id = backend_id;
220 
221   out_log(LEVEL_FLOOD,"DEBUG registered uid %d with backend %d\n",uid,backend_id);
222 
223   WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
224   return uid;
225 }
226 
227 /** \brief Update a registered user atomically. Datas are copied,
228  * and old user is freed.
229  * A pointer to the old user is still valid (change is done in-place)
230  * If the uid had changed, the user will be moved
231  * \return 0 if ok
232  */
user_update(uid_t uid,wzd_user_t * new_user)233 int user_update(uid_t uid, wzd_user_t * new_user)
234 {
235   wzd_user_t * buffer;
236 
237   if (uid == (uid_t)-1) return -1;
238   if (uid > _max_uid) return -1;
239   if (_user_array[uid] == NULL) return -2;
240 
241   if (uid != new_user->uid) {
242     if (_user_array[new_user->uid] != NULL) return -3;
243   }
244 
245   /* same user ? do nothing */
246   if (uid == new_user->uid && _user_array[uid] == new_user) return 0;
247 
248   WZD_MUTEX_LOCK(SET_MUTEX_USER);
249   /* backup old user */
250   buffer = wzd_malloc(sizeof(wzd_user_t));
251   *buffer = *_user_array[uid];
252   /* update user */
253   *_user_array[uid] = *new_user;
254   user_free(buffer);
255   if (uid != new_user->uid) {
256     _user_array[new_user->uid] = _user_array[uid];
257     _user_array[uid] = NULL;
258   }
259   WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
260 
261   return 0;
262 }
263 
264 /** \brief Unregister a user to the main server
265  * The \a user struct must be freed using user_free()
266  * \warning Unregistering a user at runtime can break the server if the user is being used
267  * \return The unregistered user structure, or NULL on error
268  */
user_unregister(uid_t uid)269 wzd_user_t * user_unregister(uid_t uid)
270 {
271   wzd_user_t * user = NULL;
272 
273   WZD_ASSERT_RETURN(uid != (uid_t)-1, NULL);
274   if (uid == (uid_t)-1) return NULL;
275 
276   if (uid > _max_uid) return NULL;
277 
278   WZD_MUTEX_LOCK(SET_MUTEX_USER);
279 
280   if (_user_array[uid] != NULL) {
281     user = _user_array[uid];
282     _user_array[uid] = NULL;
283   }
284 
285   WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
286   out_log(LEVEL_FLOOD,"DEBUG unregistered uid %d\n",uid);
287 
288   return user;
289 }
290 
291 /** \brief Free memory used to register users
292  * \warning Also free ALL registered users !
293  */
user_free_registry(void)294 void user_free_registry(void)
295 {
296   uid_t uid;
297   WZD_MUTEX_LOCK(SET_MUTEX_USER);
298   if (_user_array != NULL) {
299     for (uid=0; uid<=_max_uid; uid++) {
300       user_free(_user_array[uid]);
301     }
302   }
303   wzd_free(_user_array);
304   _user_array = NULL;
305   _max_uid = 0;
306   WZD_MUTEX_UNLOCK(SET_MUTEX_USER);
307 }
308 
309 /** \brief Get registered user using the \a uid
310  * \return The user, or NULL
311  */
user_get_by_id(uid_t uid)312 wzd_user_t * user_get_by_id(uid_t uid)
313 {
314   if (uid == (uid_t)-1) return NULL;
315   if (uid > _max_uid) return NULL;
316   if (_max_uid == 0) return NULL;
317 
318   return _user_array[uid];
319 }
320 
321 /** \brief Get registered user using the \a name
322  * \return The user, or NULL
323  * \todo Re-implement the function using a hash table
324  */
user_get_by_name(const char * username)325 wzd_user_t * user_get_by_name(const char * username)
326 {
327   uid_t uid;
328 
329   if (username == NULL || strlen(username)<1 || _max_uid==0) return NULL;
330 
331   /* We don't need to lock the access since the _user_array can only grow */
332   for (uid=0; uid<=_max_uid; uid++) {
333     if (_user_array[uid] != NULL
334         && _user_array[uid]->username != NULL
335         && strcmp(username,_user_array[uid]->username)==0)
336       return _user_array[uid];
337   }
338   return NULL;
339 }
340 
341 /** \brief Get list or users register for a specific backend
342  * The returned list is terminated by -1, and must be freed with wzd_free()
343  */
user_get_list(u16_t backend_id)344 uid_t * user_get_list(u16_t backend_id)
345 {
346   uid_t * uid_list = NULL;
347   uid_t size;
348   int index;
349   uid_t uid;
350 
351 /*  WZD_MUTEX_LOCK(SET_MUTEX_USER);*/
352 
353   /** \todo it would be better to get the real number of used uid */
354   size = _max_uid;
355 
356   uid_list = (uid_t*)wzd_malloc((size+1)*sizeof(uid_t));
357   index = 0;
358   /* We don't need to lock the access since the _user_array can only grow */
359   for (uid=0; uid<size; uid++) {
360     if (_user_array[uid] != NULL
361         && _user_array[uid]->uid != INVALID_USER)
362       uid_list[index++] = _user_array[uid]->uid;
363   }
364   uid_list[index] = (uid_t)-1;
365   uid_list[size] = (uid_t)-1;
366 
367 /*  WZD_MUTEX_UNLOCK(SET_MUTEX_USER);*/
368 
369   return uid_list;
370 }
371 
372 /** \brief Find the first free uid, starting from \a start
373  */
user_find_free_uid(uid_t start)374 uid_t user_find_free_uid(uid_t start)
375 {
376   uid_t uid;
377 
378   if (start == (uid_t)-1) start = 0;
379 
380   /** \todo locking may be harmful if this function is called from another
381    * user_x() function
382    */
383 /*  WZD_MUTEX_LOCK(SET_MUTEX_USER);*/
384   for (uid = start; uid < _max_uid && uid != (uid_t)-1; uid++) {
385     if (_user_array[uid] == NULL) break;
386   }
387 /*  WZD_MUTEX_UNLOCK(SET_MUTEX_USER);*/
388 
389   return uid;
390 }
391 
392 /** \brief Add an ip to the list of authorized/forbidden ips
393  * \return 0 if ok
394  */
user_ip_add(wzd_user_t * user,const char * ip,int is_authorized)395 int user_ip_add(wzd_user_t * user, const char * ip, int is_authorized)
396 {
397   WZD_ASSERT( user != NULL );
398   if (user == NULL) return -1;
399 
400   /** \note The number of stored ips per user is no more limited */
401 
402   return ip_add_check(&user->ip_list, ip, is_authorized);
403 }
404 
405 /** \brief List all users in a particular group, optionally filtered by a flag
406  *
407  * Optional: a flag can be specified where only users with this flag set will be returned (use 0 to ignore)
408  * \return
409  *  - a user list terminated by -1, must be freed with wzd_free()
410  *  - NULL if no group with that gid was found
411  */
group_list_users(gid_t gid,char flag)412 uid_t * group_list_users(gid_t gid, char flag /* optional */)
413 {
414   uid_t * uid_list = NULL;
415   uid_t size;
416   int index;
417   uid_t uid;
418   int groups;
419 
420   /* Check that the supplied gid is valid */
421   if (group_get_by_id(gid) == NULL)
422     return NULL;
423 
424 /*  WZD_MUTEX_LOCK(SET_MUTEX_USER);*/
425 
426   /** \todo it would be better to get the real number of used uid */
427   size = _max_uid;
428 
429   uid_list = (uid_t*)wzd_malloc((size+1)*sizeof(uid_t));
430   index = 0;
431   /* We don't need to lock the access since the _user_array can only grow */
432   for (uid=0; uid<size; uid++) {
433     if (_user_array[uid] != NULL && _user_array[uid]->uid != INVALID_USER) {
434       for (groups=0; groups<MAX_GROUPS_PER_USER; groups++) {
435         if (_user_array[uid]->groups[groups] == gid) {
436           /* Check if the user has a certain flag */
437           if (flag == 0 || strchr(_user_array[uid]->flags,flag)!=NULL) {
438             uid_list[index++] = _user_array[uid]->uid;
439             /* Found a match, stop cycling through groups list! */
440             groups = MAX_GROUPS_PER_USER;
441           }
442         }
443       }
444     }
445   }
446   uid_list[index] = (uid_t)-1;
447   uid_list[size] = (uid_t)-1;
448 
449 /*  WZD_MUTEX_UNLOCK(SET_MUTEX_USER);*/
450 
451   return uid_list;
452 }
453 
454