Line data Source code
1 : /*
2 : SSSD
3 :
4 : Create uid table
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include <stdio.h>
26 : #include <sys/types.h>
27 : #include <dirent.h>
28 : #include <errno.h>
29 : #include <sys/stat.h>
30 : #include <unistd.h>
31 : #include <fcntl.h>
32 : #include <stdlib.h>
33 : #include <string.h>
34 : #include <limits.h>
35 : #include <talloc.h>
36 : #include <ctype.h>
37 : #include <sys/time.h>
38 : #include <dhash.h>
39 :
40 : #include "util/find_uid.h"
41 : #include "util/util.h"
42 : #include "util/strtonum.h"
43 :
44 : #ifdef HAVE_SYSTEMD_LOGIN
45 : #include <systemd/sd-login.h>
46 : #endif
47 :
48 : #define INITIAL_TABLE_SIZE 64
49 : #define PATHLEN (NAME_MAX + 14)
50 : #define BUFSIZE 4096
51 :
52 36 : static void *hash_talloc(const size_t size, void *pvt)
53 : {
54 36 : return talloc_size(pvt, size);
55 : }
56 :
57 1 : static void hash_talloc_free(void *ptr, void *pvt)
58 : {
59 1 : talloc_free(ptr);
60 1 : }
61 :
62 1328 : static errno_t get_uid_from_pid(const pid_t pid, uid_t *uid)
63 : {
64 : int ret;
65 : char path[PATHLEN];
66 : struct stat stat_buf;
67 : int fd;
68 : char buf[BUFSIZE];
69 : char *p;
70 : char *e;
71 : char *endptr;
72 1328 : uint32_t num=0;
73 : errno_t error;
74 :
75 1328 : ret = snprintf(path, PATHLEN, "/proc/%d/status", pid);
76 1328 : if (ret < 0) {
77 0 : DEBUG(SSSDBG_CRIT_FAILURE, "snprintf failed\n");
78 0 : return EINVAL;
79 1328 : } else if (ret >= PATHLEN) {
80 0 : DEBUG(SSSDBG_CRIT_FAILURE, "path too long?!?!\n");
81 0 : return EINVAL;
82 : }
83 :
84 1328 : fd = open(path, O_RDONLY);
85 1328 : if (fd == -1) {
86 0 : error = errno;
87 0 : if (error == ENOENT) {
88 0 : DEBUG(SSSDBG_TRACE_LIBS,
89 : "Proc file [%s] is not available anymore, continuing.\n",
90 : path);
91 0 : return EOK;
92 : }
93 0 : DEBUG(SSSDBG_CRIT_FAILURE,
94 : "open failed [%d][%s].\n", error, strerror(error));
95 0 : return error;
96 : }
97 :
98 1328 : ret = fstat(fd, &stat_buf);
99 1328 : if (ret == -1) {
100 0 : error = errno;
101 0 : if (error == ENOENT) {
102 0 : DEBUG(SSSDBG_TRACE_LIBS,
103 : "Proc file [%s] is not available anymore, continuing.\n",
104 : path);
105 0 : error = EOK;
106 0 : goto fail_fd;
107 : }
108 0 : DEBUG(SSSDBG_CRIT_FAILURE,
109 : "fstat failed [%d][%s].\n", error, strerror(error));
110 0 : goto fail_fd;
111 : }
112 :
113 1328 : if (!S_ISREG(stat_buf.st_mode)) {
114 0 : DEBUG(SSSDBG_CRIT_FAILURE, "not a regular file\n");
115 0 : error = EINVAL;
116 0 : goto fail_fd;
117 : }
118 :
119 1328 : errno = 0;
120 1328 : ret = sss_atomic_read_s(fd, buf, BUFSIZE);
121 1328 : if (ret == -1) {
122 0 : error = errno;
123 0 : DEBUG(SSSDBG_CRIT_FAILURE,
124 : "read failed [%d][%s].\n", error, strerror(error));
125 0 : goto fail_fd;
126 : }
127 :
128 : /* Guarantee NULL-termination in case we read the full BUFSIZE somehow */
129 1328 : buf[BUFSIZE-1] = '\0';
130 :
131 1328 : ret = close(fd);
132 1328 : if (ret == -1) {
133 0 : error = errno;
134 0 : DEBUG(SSSDBG_CRIT_FAILURE,
135 : "close failed [%d][%s].\n", error, strerror(error));
136 : }
137 :
138 1328 : p = strstr(buf, "\nUid:\t");
139 1328 : if (p != NULL) {
140 1328 : p += 6;
141 1328 : e = strchr(p,'\t');
142 1328 : if (e == NULL) {
143 0 : DEBUG(SSSDBG_CRIT_FAILURE, "missing delimiter.\n");
144 0 : return EINVAL;
145 : } else {
146 1328 : *e = '\0';
147 : }
148 1328 : num = (uint32_t) strtoint32(p, &endptr, 10);
149 1328 : error = errno;
150 1328 : if (error != 0) {
151 0 : DEBUG(SSSDBG_CRIT_FAILURE,
152 : "strtol failed [%s].\n", strerror(error));
153 0 : return error;
154 : }
155 1328 : if (*endptr != '\0') {
156 0 : DEBUG(SSSDBG_CRIT_FAILURE, "uid contains extra characters\n");
157 0 : return EINVAL;
158 : }
159 :
160 : } else {
161 0 : DEBUG(SSSDBG_CRIT_FAILURE, "format error\n");
162 0 : return EINVAL;
163 : }
164 :
165 1328 : *uid = num;
166 :
167 1328 : return EOK;
168 :
169 : fail_fd:
170 0 : close(fd);
171 0 : return error;
172 : }
173 :
174 1328 : static errno_t name_to_pid(const char *name, pid_t *pid)
175 : {
176 : long num;
177 : char *endptr;
178 : errno_t error;
179 :
180 1328 : errno = 0;
181 1328 : num = strtol(name, &endptr, 10);
182 1328 : error = errno;
183 1328 : if (error == ERANGE) {
184 0 : perror("strtol");
185 0 : return error;
186 : }
187 :
188 1328 : if (*endptr != '\0') {
189 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pid string contains extra characters.\n");
190 0 : return EINVAL;
191 : }
192 :
193 1328 : if (num <= 0 || num >= INT_MAX) {
194 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pid out of range.\n");
195 0 : return ERANGE;
196 : }
197 :
198 1328 : *pid = num;
199 :
200 1328 : return EOK;
201 : }
202 :
203 1576 : static int only_numbers(char *p)
204 : {
205 1576 : while(*p!='\0' && isdigit(*p)) ++p;
206 1576 : return *p;
207 : }
208 :
209 4 : static errno_t get_active_uid_linux(hash_table_t *table, uid_t search_uid)
210 : {
211 4 : DIR *proc_dir = NULL;
212 : struct dirent *dirent;
213 : int ret, err;
214 4 : pid_t pid = -1;
215 : uid_t uid;
216 :
217 : hash_key_t key;
218 : hash_value_t value;
219 :
220 4 : proc_dir = opendir("/proc");
221 4 : if (proc_dir == NULL) {
222 0 : ret = errno;
223 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot open proc dir.\n");
224 0 : goto done;
225 : };
226 :
227 4 : errno = 0;
228 1584 : while ((dirent = readdir(proc_dir)) != NULL) {
229 1576 : if (only_numbers(dirent->d_name) != 0) continue;
230 1328 : ret = name_to_pid(dirent->d_name, &pid);
231 1328 : if (ret != EOK) {
232 0 : DEBUG(SSSDBG_CRIT_FAILURE, "name_to_pid failed.\n");
233 0 : goto done;
234 : }
235 :
236 1328 : ret = get_uid_from_pid(pid, &uid);
237 1328 : if (ret != EOK) {
238 0 : DEBUG(SSSDBG_CRIT_FAILURE, "get_uid_from_pid failed.\n");
239 0 : goto done;
240 : }
241 :
242 1328 : if (table != NULL) {
243 664 : key.type = HASH_KEY_ULONG;
244 664 : key.ul = (unsigned long) uid;
245 664 : value.type = HASH_VALUE_ULONG;
246 664 : value.ul = (unsigned long) uid;
247 :
248 664 : ret = hash_enter(table, &key, &value);
249 664 : if (ret != HASH_SUCCESS) {
250 0 : DEBUG(SSSDBG_CRIT_FAILURE,
251 : "cannot add to table [%s]\n", hash_error_string(ret));
252 0 : ret = ENOMEM;
253 0 : goto done;
254 : }
255 : } else {
256 664 : if (uid == search_uid) {
257 0 : ret = EOK;
258 0 : goto done;
259 : }
260 : }
261 :
262 :
263 1328 : errno = 0;
264 : }
265 4 : if (errno != 0 && dirent == NULL) {
266 0 : ret = errno;
267 0 : DEBUG(SSSDBG_CRIT_FAILURE, "readdir failed.\n");
268 0 : goto done;
269 : }
270 :
271 4 : ret = closedir(proc_dir);
272 4 : proc_dir = NULL;
273 4 : if (ret == -1) {
274 0 : DEBUG(SSSDBG_CRIT_FAILURE, "closedir failed, watch out.\n");
275 : }
276 :
277 4 : if (table != NULL) {
278 2 : ret = EOK;
279 : } else {
280 2 : ret = ENOENT;
281 : }
282 :
283 : done:
284 4 : if (proc_dir != NULL) {
285 0 : err = closedir(proc_dir);
286 0 : if (err) {
287 0 : DEBUG(SSSDBG_CRIT_FAILURE, "closedir failed, bad dirp?\n");
288 : }
289 : }
290 4 : return ret;
291 : }
292 :
293 2 : errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table)
294 : {
295 : #ifdef __linux__
296 : int ret;
297 :
298 2 : ret = hash_create_ex(INITIAL_TABLE_SIZE, table, 0, 0, 0, 0,
299 : hash_talloc, hash_talloc_free, mem_ctx,
300 : NULL, NULL);
301 2 : if (ret != HASH_SUCCESS) {
302 0 : DEBUG(SSSDBG_CRIT_FAILURE,
303 : "hash_create_ex failed [%s]\n", hash_error_string(ret));
304 0 : return ENOMEM;
305 : }
306 :
307 2 : return get_active_uid_linux(*table, 0);
308 : #else
309 : return ENOSYS;
310 : #endif
311 : }
312 :
313 4 : errno_t check_if_uid_is_active(uid_t uid, bool *result)
314 : {
315 : int ret;
316 :
317 : #ifdef HAVE_SYSTEMD_LOGIN
318 4 : ret = sd_uid_get_sessions(uid, 0, NULL);
319 4 : if (ret > 0) {
320 2 : *result = true;
321 2 : return EOK;
322 : }
323 2 : if (ret == 0) {
324 2 : *result = false;
325 : }
326 2 : if (ret < 0) {
327 0 : DEBUG(SSSDBG_CRIT_FAILURE, "systemd-login gave error %d: %s\n",
328 : -ret, strerror(-ret));
329 : }
330 : /* fall back to the old method */
331 : #endif
332 :
333 2 : ret = get_active_uid_linux(NULL, uid);
334 2 : if (ret != EOK && ret != ENOENT) {
335 0 : DEBUG(SSSDBG_CRIT_FAILURE, "get_uid_table failed.\n");
336 0 : return ret;
337 : }
338 :
339 2 : if (ret == EOK) {
340 0 : *result = true;
341 : } else {
342 2 : *result = false;
343 : }
344 :
345 2 : return EOK;
346 : }
|