source: trunk/src/sh_subuid.c@ 536

Last change on this file since 536 was 534, checked in by katerina, 6 years ago

Version 4.3.0, support for /etc/subuid, /etc/subgid

File size: 5.9 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2018 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22#undef FIL__
23#define FIL__ _("sh_subuid.c")
24
25#if defined(__linux__)
26
27#include <sys/types.h>
28#include <time.h>
29#include <sys/stat.h>
30#include <unistd.h>
31
32#include <stdlib.h>
33#include <errno.h>
34#include <limits.h>
35
36#include "samhain.h"
37#include "sh_unix.h"
38
39#define SH_SUBUID_FILE _("/etc/subuid")
40#define SH_SUBGID_FILE _("/etc/subgid")
41
42struct subuid_t {
43 char name[32];
44 unsigned long first;
45 unsigned long last;
46 struct subuid_t * next;
47};
48
49static time_t last_subuid = 0;
50static time_t last_subgid = 0;
51
52struct subuid_t * list_subuid = NULL;
53struct subuid_t * list_subgid = NULL;
54
55/* Check whether we need to re-read the subuid/subgid file
56 */
57static int needs_reread (char * file, time_t * last)
58{
59 int retval = S_FALSE;
60 struct stat buf;
61 int status = retry_lstat (FIL__, __LINE__, file, &buf);
62
63 if (status == 0)
64 {
65 if ((buf.st_mtime - *last) > 1)
66 {
67 *last = buf.st_mtime;
68 retval = S_TRUE;
69 }
70 }
71 else if (status && errno == ENOENT)
72 {
73 /* If there was a file make sure we attempt to re-read
74 * to zero out the list.
75 */
76 if (*last > 0) retval = S_TRUE;
77 *last = 0;
78 }
79 return retval;
80}
81
82/* free the whole list
83 */
84static void free_subordinate(struct subuid_t * head)
85{
86 struct subuid_t * prev;
87 struct subuid_t * curr = head;
88
89 while (curr)
90 {
91 prev = curr;
92 curr = curr->next;
93 SH_FREE(prev);
94 }
95 return;
96}
97
98#define NFIELDS_SUBUID 3
99
100static int get_ulong(char * str, unsigned long * result)
101{
102 char * endptr;
103
104 errno = 0;
105 *result = strtoul(str, &endptr, 0);
106 if (*str != '\0' && *endptr == '\0' && errno != ERANGE)
107 return S_TRUE;
108 return S_FALSE;
109}
110
111/* Parse a single line into name / startuid / lastuid
112 */
113static struct subuid_t * parse_subordinate(char * line)
114{
115 unsigned int nfields = NFIELDS_SUBUID;
116 size_t lengths[NFIELDS_SUBUID];
117 unsigned long start, count;
118 struct subuid_t * new;
119
120 char ** array = split_array(line, &nfields, ':', lengths);
121
122 if (nfields != NFIELDS_SUBUID)
123 { SH_FREE(array); return NULL; }
124
125 if (S_TRUE != get_ulong(array[1], &start))
126 { SH_FREE(array); return NULL; }
127 if ((S_TRUE != get_ulong(array[2], &count)) || (count == 0))
128 { SH_FREE(array); return NULL; }
129 if (lengths[0] == 0)
130 { SH_FREE(array); return NULL; }
131
132 /* we have checked that count != 0 */
133 --count;
134
135 if (start > (ULONG_MAX - count))
136 { SH_FREE(array); return NULL; }
137
138 new = SH_ALLOC(sizeof(struct subuid_t));
139 sl_strlcpy(new->name, array[0], 32);
140 new->first = start;
141 new->last = start + count; /* start+count-1, but we already did --count */
142 new->next = NULL;
143
144 SH_FREE(array);
145 return new;
146}
147
148/* (re-)read the subuid/subgid file
149 */
150static void reread_subordinate (char * file, struct subuid_t ** head_ref)
151{
152 SL_TICKET fd = (-1);
153 char line[1024];
154
155 if (*head_ref) { free_subordinate(*head_ref); *head_ref = NULL; }
156
157 fd = sl_open_read (FIL__, __LINE__, file, SL_YESPRIV);
158 if (!SL_ISERROR(fd))
159 {
160 while ( sh_unix_getline(fd, line, sizeof(line)) > 0 )
161 {
162 /* for invalid lines, NULL will be returned
163 */
164 struct subuid_t * new = parse_subordinate(line);
165
166 if (new)
167 {
168 new->next = *head_ref;
169 *head_ref = new;
170 }
171 }
172 sl_close(fd);
173 }
174 return;
175}
176
177/* Return the username for a given subuid/subgid
178 */
179static char * get_name4id (unsigned long id, struct subuid_t * head)
180{
181 struct subuid_t * cur = head;
182
183 while (cur)
184 {
185 if (id >= cur->first && id <= cur->last)
186 return cur->name;
187 cur = cur->next;
188 }
189 return NULL;
190}
191
192/***********************************************
193 *
194 * Public functions
195 *
196 ***********************************************/
197
198/* Returns username or NULL for a subuid
199 */
200char * sh_get_subuid (unsigned long subuid)
201{
202 static int init = 0;
203 static char file[256];
204
205 if (!init) { sl_strlcpy(file, SH_SUBUID_FILE, sizeof(file)); init = 1; }
206
207 if (S_TRUE == needs_reread(file, &last_subuid))
208 reread_subordinate(file, &list_subuid);
209
210 return get_name4id (subuid, list_subuid);
211}
212
213/* Returns group name or NULL for subgid
214 */
215char * sh_get_subgid (unsigned long subgid)
216{
217 static int init = 0;
218 static char file[256];
219
220 if (!init) { sl_strlcpy(file, SH_SUBGID_FILE, sizeof(file)); init = 1; }
221
222 if (S_TRUE == needs_reread(file, &last_subgid))
223 reread_subordinate(file, &list_subgid);
224
225 return get_name4id (subgid, list_subgid);
226}
227
228/* Not Linux, hence no sub(u|g)id
229 */
230#else
231
232char * sh_get_subuid (unsigned long subuid)
233{
234 return NULL;
235}
236
237char * sh_get_subgid (unsigned long subgid)
238{
239 return NULL;
240}
241
242#endif
Note: See TracBrowser for help on using the repository browser.