1 /*
2  * File: sh_userfiles.c
3  * Desc: A module for Samhain; adds files in user directories to the check list
4  * Auth: Jerry Connolly <jerry.connolly@eircom.net>
5  */
6 /*  This program is free software; you can redistribute it                 */
7 /*  and/or modify                                                          */
8 /*  it under the terms of the GNU General Public License as                */
9 /*  published by                                                           */
10 /*  the Free Software Foundation; either version 2 of the License, or      */
11 /*  (at your option) any later version.                                    */
12 /*                                                                         */
13 /*  This program is distributed in the hope that it will be useful,        */
14 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
15 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
16 /*  GNU General Public License for more details.                           */
17 /*                                                                         */
18 /*  You should have received a copy of the GNU General Public License      */
19 /*  along with this program; if not, write to the Free Software            */
20 /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
21 
22 #include "config_xor.h"
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <limits.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <pwd.h>
31 
32 #include "samhain.h"
33 #include "sh_modules.h"
34 #include "sh_userfiles.h"
35 #include "sh_utils.h"
36 #include "sh_schedule.h"
37 #include "sh_error.h"
38 #include "sh_hash.h"
39 #include "sh_files.h"
40 #define SH_NEED_PWD_GRP 1
41 #include "sh_static.h"
42 #include "sh_pthread.h"
43 
44 #ifdef SH_USE_USERFILES
45 
46 #define FIL__  _("sh_userfiles.c")
47 
48 /* We won't want to build this into yule */
49 #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
50 
51 static int    ShUserfilesActive   = S_TRUE;
52 
53 struct userfileslist {
54     char filename[PATH_MAX];
55     int level;
56 
57     struct userfileslist *next;
58 };
59 
60 struct userhomeslist {
61     char *pw_dir;
62 
63     struct userhomeslist *next;
64 };
65 
66 struct useruidlist {
67   unsigned long lower;
68   unsigned long upper;
69   struct useruidlist *next;
70 };
71 
72 static struct userfileslist *userFiles = NULL;
73 static struct userhomeslist *userHomes = NULL;
74 static struct useruidlist   *userUids  = NULL;
75 
76 static void sh_userfiles_free_fileslist(struct userfileslist *head);
77 static void sh_userfiles_free_homeslist(struct userhomeslist *head);
78 static void sh_userfiles_free_uidslist (struct useruidlist   *head);
79 
80 sh_rconf sh_userfiles_table[] = {
81     {
82         N_("userfilesname"),
83         sh_userfiles_add_file,
84     },
85     {
86         N_("userfilesactive"),
87         sh_userfiles_set_active,
88     },
89     {
90         N_("userfilescheckuids"),
91         sh_userfiles_set_uid,
92     },
93     {
94         NULL,
95         NULL
96     }
97 };
98 
sh_userfiles_check_uid(unsigned long uid)99 static int sh_userfiles_check_uid (unsigned long uid)
100 {
101   struct useruidlist * uids = userUids;
102 
103   /* default is to include all
104    */
105   if (userUids == NULL)
106     return 1;
107 
108   while (uids)
109     {
110       if ((uids->upper != 0) && (uid >= uids->lower) && (uid <= uids->upper))
111 	return 1;
112       if ((uids->upper == 0) && (uid == uids->lower))
113 	return 1;
114       uids = uids->next;
115     }
116   return 0;
117 }
118 
sh_userfiles_set_uid(const char * str)119 int sh_userfiles_set_uid (const char * str)
120 {
121   char * end;
122   const  char * p = str;
123   unsigned long lower;
124   unsigned long upper = 0;
125   struct useruidlist * uids;
126 
127   while ((p != NULL) && (*p != '\0'))
128     {
129       lower = strtoul(p, &end, 10);
130       if ( (lower == ULONG_MAX) || (end == p))
131 	return -1;
132       p = end;
133       if (*p == '-')
134 	{
135 	  ++p;
136 	  if (*p == '\0')
137 	    {
138 	      upper = ULONG_MAX;
139 	      p     = NULL;
140 	    }
141 	  else
142 	    {
143 	      upper = strtoul(p, &end, 10);
144 	      if ( (upper == ULONG_MAX) || (end == p))
145 		return -1;
146 	      p = end;
147 	      if ( (*p != ',') && (*p != '\0'))
148 		return -1;
149 	      if (*p != '\0')
150 		++p;
151 	    }
152 	}
153       else if (*p == '\0')
154 	{
155 	  upper = 0;
156 	  p     = NULL;
157 	}
158       else if ((*p == ',') || (*p == ' ') || (*p == '\t'))
159 	{
160 	  upper = 0;
161 	  ++p;
162 	}
163       else
164 	{
165 	  upper = strtoul(p, &end, 10);
166 	  if ( (upper == ULONG_MAX) || (end == p))
167 	    return -1;
168 	  p = end;
169 	  if ( (*p != ',') && (*p != ' ') && (*p != '\t') && (*p != '\0') )
170 	    return -1;
171 	  if (*p != '\0')
172 	    ++p;
173 	}
174       uids = SH_ALLOC(sizeof(struct useruidlist));
175       uids->lower = lower;
176       uids->upper = upper;
177       uids->next  = userUids;
178       userUids = uids;
179       /* fprintf(stderr, "range %lu %lu\n", lower, upper); */
180     }
181   return 0;
182 }
183 
184 /* Add 'c' to the list of files (userFiles) relative to any given HOME
185  * directory that should be checked. */
186 
sh_userfiles_add_file(const char * c)187 int sh_userfiles_add_file(const char *c) {
188     struct userfileslist *new;
189     char *s, *orig;
190     char *user_filename;
191 
192     int  default_level = SH_LEVEL_NOIGNORE;
193     char *separator = " ";
194 
195     SL_ENTER(_("sh_userfiles_add_file"));
196 
197     if( c == NULL )
198       SL_RETURN(-1, _("sh_userfiles_add_file") );
199 
200     s = sh_util_strdup(c); /* Maybe c is needed elsewhere */
201     orig = s;
202 
203     user_filename = sh_util_strsep(&s, separator);
204 
205     if( user_filename == NULL || strlen(user_filename) > PATH_MAX )
206       SL_RETURN(-1, _("sh_userfiles_add_file") );
207 
208     new = SH_ALLOC(sizeof(struct userfileslist));
209 
210     (void) sl_strlcpy(new->filename, user_filename, PATH_MAX);
211     new->next = userFiles;
212     userFiles = new;
213 
214     /* order is important here, since 'log' would match on 'glog'
215      * So, compare longest strings first */
216     if( s == NULL ) /* The default */          new->level = default_level;
217     else if ( strstr(s, _("attributes"))!= NULL ) new->level = SH_LEVEL_ATTRIBUTES;
218     else if ( strstr(s, _("allignore")) != NULL ) new->level = SH_LEVEL_ALLIGNORE;
219     else if ( strstr(s, _("noignore"))  != NULL ) new->level = SH_LEVEL_NOIGNORE;
220     else if ( strstr(s, _("logfiles"))  != NULL ) new->level = SH_LEVEL_LOGFILES;
221     else if ( strstr(s, _("readonly"))  != NULL ) new->level = SH_LEVEL_READONLY;
222     else if ( strstr(s, _("loggrow"))   != NULL ) new->level = SH_LEVEL_LOGGROW;
223     else if ( strstr(s, _("user0"))     != NULL ) new->level = SH_LEVEL_USER0;
224     else if ( strstr(s, _("user1"))     != NULL ) new->level = SH_LEVEL_USER1;
225     else if ( strstr(s, _("user2"))     != NULL ) new->level = SH_LEVEL_USER2;
226     else if ( strstr(s, _("user3"))     != NULL ) new->level = SH_LEVEL_USER3;
227     else if ( strstr(s, _("user4"))     != NULL ) new->level = SH_LEVEL_USER4;
228     else if ( strstr(s, _("prelink"))   != NULL ) new->level = SH_LEVEL_PRELINK;
229     else            /* The default */          new->level = default_level;
230 
231     SH_FREE(orig);
232 
233     SL_RETURN(0, _("sh_userfiles_add_file") );
234 }
235 
236 /* Decide if we're active.
237  */
sh_userfiles_set_active(const char * c)238 int sh_userfiles_set_active(const char *c) {
239     int value;
240 
241     SL_ENTER(_("sh_userfiles_set_active"));
242     value = sh_util_flagval(c, &ShUserfilesActive);
243     SL_RETURN((value), _("sh_userfiles_set_active"));
244 }
245 
246 /* Build the list of users, then use this to construct the filenames to
247  * be checked. */
sh_userfiles_init(struct mod_type * arg)248 int sh_userfiles_init(struct mod_type * arg) {
249     struct passwd *cur_user;
250     struct userhomeslist *end;
251     struct userhomeslist *new;
252     struct userhomeslist *homes;
253     char * filepath;
254     (void) arg;
255 
256     SL_ENTER(_("sh_userfiles_init"));
257 
258     /* We need to free anything allocated by the configuration functions if
259      * we find that the module is to be left inactive - otherwise _reconf()
260      * won't quite work. */
261     if( ShUserfilesActive == S_FALSE ) {
262       sh_userfiles_free_homeslist(userHomes);
263       sh_userfiles_free_fileslist(userFiles);
264       userHomes = NULL;
265       userFiles = NULL;
266       SL_RETURN(-1, _("sh_userfiles_init"));
267     }
268 
269     /* We build a list in here because the samhain internals want to use
270      * getpwent() too */
271     SH_MUTEX_LOCK(mutex_pwent);
272     /*@-unrecog@*/
273     sh_setpwent();
274     /*@+unrecog@*/
275     while( ( cur_user = /*@-unrecog@*/sh_getpwent()/*@+unrecog@*/ ) != NULL ) {
276         int found = 0;
277 
278 	if (0 == sh_userfiles_check_uid( (unsigned long) cur_user->pw_uid))
279 	  continue;
280 
281         for( end = userHomes; end != NULL; end = end->next ) {
282             if( sl_strcmp( end->pw_dir, cur_user->pw_dir) == 0 ) {
283                 found = 1; /* Found a match, so flag it and stop searching */
284                 break;
285             }
286         }
287 
288         if( found == 0 ) {
289             /* Didn't find it, so add to the front of the list */
290             new = SH_ALLOC(sizeof(struct userhomeslist) );
291             new->next = userHomes;
292             new->pw_dir = sh_util_strdup(cur_user->pw_dir);
293 
294             userHomes = new;
295         }
296     }
297     sh_endpwent();
298     SH_MUTEX_UNLOCK(mutex_pwent);
299 
300     filepath = SH_ALLOC(PATH_MAX);
301 
302     for (homes = userHomes; homes != NULL; homes = homes->next ) {
303         struct userfileslist *file_ptr;
304 
305         for (file_ptr = userFiles; file_ptr != NULL; file_ptr = file_ptr->next) {
306             (void) sl_strncpy(filepath, homes->pw_dir, PATH_MAX);
307             (void) sl_strncat(filepath, "/", PATH_MAX);
308             (void) sl_strncat(filepath, file_ptr->filename, PATH_MAX);
309 
310             switch(file_ptr->level) {
311                 case SH_LEVEL_READONLY:
312                     (void) sh_files_pushfile_ro(filepath);
313                     break;
314                 case SH_LEVEL_LOGFILES:
315                     (void) sh_files_pushfile_log(filepath);
316                     break;
317                 case SH_LEVEL_LOGGROW:
318                     (void) sh_files_pushfile_glog(filepath);
319                     break;
320                 case SH_LEVEL_NOIGNORE:
321                     (void) sh_files_pushfile_noig(filepath);
322                     break;
323                 case SH_LEVEL_ALLIGNORE:
324                     (void) sh_files_pushfile_allig(filepath);
325                     break;
326                 case SH_LEVEL_ATTRIBUTES:
327                     (void) sh_files_pushfile_attr(filepath);
328                     break;
329                 case SH_LEVEL_USER0:
330                     (void) sh_files_pushfile_user0(filepath);
331                     break;
332                 case SH_LEVEL_USER1:
333                     (void) sh_files_pushfile_user1(filepath);
334                     break;
335                 case SH_LEVEL_USER2:
336                     (void) sh_files_pushfile_user2(filepath);
337                     break;
338                 case SH_LEVEL_USER3:
339                     (void) sh_files_pushfile_user3(filepath);
340                     break;
341                 case SH_LEVEL_USER4:
342                     (void) sh_files_pushfile_user4(filepath);
343                     break;
344                 case SH_LEVEL_PRELINK:
345                     (void) sh_files_pushfile_prelink(filepath);
346                     break;
347                 default: /* Should not reach here */
348                     break;
349             }
350         }
351     }
352 
353     SH_FREE(filepath);
354 
355     SL_RETURN(0, _("sh_userfiles_init"));
356 }
357 
358 /* This is pretty much NULL; we don't do anything in our checking routine,
359  * so we never need to run it. Just use tcurrent to avoid compiler warnings. */
sh_userfiles_timer(time_t tcurrent)360 int sh_userfiles_timer(time_t tcurrent) {
361     SL_ENTER(_("sh_userfiles_timer"));
362     tcurrent = 0;
363     SL_RETURN((int)tcurrent, _("sh_userfiles_timer"));
364 }
365 
sh_userfiles_check(void)366 int sh_userfiles_check(void) {
367     SL_ENTER(_("sh_userfiles_check"));
368     SL_RETURN(0, _("sh_userfiles_check"));
369 }
370 
371 /* Free our lists and the associated memory */
372 
sh_userfiles_cleanup(void)373 int sh_userfiles_cleanup(void) {
374     SL_ENTER(_("sh_userfiles_cleanup"));
375 
376     sh_userfiles_free_homeslist(userHomes);
377     sh_userfiles_free_fileslist(userFiles);
378     sh_userfiles_free_uidslist (userUids);
379 
380     SL_RETURN(0, _("sh_userfiles_cleanup"));
381 }
382 
383 /* As with sh_userfiles_cleanup, but in preparation for re-reading the
384  * configuration files */
385 
sh_userfiles_reconf(void)386 int sh_userfiles_reconf(void) {
387   SL_ENTER(_("sh_userfiles_reconf"));
388 
389     sh_userfiles_free_homeslist(userHomes);
390     sh_userfiles_free_fileslist(userFiles);
391     sh_userfiles_free_uidslist (userUids);
392 
393     userHomes = NULL;
394     userFiles = NULL;
395     userUids  = NULL;
396 
397     ShUserfilesActive   = S_TRUE;
398 
399     SL_RETURN(0, _("sh_userfiles_reconf"));
400 }
401 
402 /* Recurse to the end of the list and then free the data as we return
403  * back up towards the start, making sure to free any strdupped strings
404  */
405 
sh_userfiles_free_homeslist(struct userhomeslist * head)406 static void sh_userfiles_free_homeslist(struct userhomeslist *head) {
407     if( head != NULL ) {
408         sh_userfiles_free_homeslist(head->next);
409         SH_FREE(head->pw_dir);
410         SH_FREE(head);
411     }
412 }
413 
414 /* Recurse to the end of the list and then free the data as we return
415  * back up towards the start */
416 
sh_userfiles_free_fileslist(struct userfileslist * head)417 static void sh_userfiles_free_fileslist(struct userfileslist *head) {
418     if( head != NULL ) {
419         sh_userfiles_free_fileslist(head->next);
420         SH_FREE(head);
421     }
422 }
423 
424 /* Recurse to the end of the list and then free the data as we return
425  * back up towards the start */
426 
sh_userfiles_free_uidslist(struct useruidlist * head)427 static void sh_userfiles_free_uidslist(struct useruidlist *head) {
428     if( head != NULL ) {
429         sh_userfiles_free_uidslist(head->next);
430         SH_FREE(head);
431     }
432 }
433 
434 /* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
435 #endif
436 
437 /* #ifdef SH_USE_USERFILES */
438 #endif
439