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 30 : static void *hash_talloc(const size_t size, void *pvt)
53 : {
54 30 : 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 874 : 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 874 : uint32_t num=0;
73 : errno_t error;
74 :
75 874 : ret = snprintf(path, PATHLEN, "/proc/%d/status", pid);
76 874 : if (ret < 0) {
77 0 : DEBUG(SSSDBG_CRIT_FAILURE, "snprintf failed\n");
78 0 : return EINVAL;
79 874 : } else if (ret >= PATHLEN) {
80 0 : DEBUG(SSSDBG_CRIT_FAILURE, "path too long?!?!\n");
81 0 : return EINVAL;
82 : }
83 :
84 874 : fd = open(path, O_RDONLY);
85 874 : 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 874 : ret = fstat(fd, &stat_buf);
99 874 : 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 874 : 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 874 : errno = 0;
120 874 : ret = sss_atomic_read_s(fd, buf, BUFSIZE);
121 874 : 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 874 : buf[BUFSIZE-1] = '\0';
130 :
131 874 : ret = close(fd);
132 874 : 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 874 : p = strstr(buf, "\nUid:\t");
139 874 : if (p != NULL) {
140 874 : p += 6;
141 874 : e = strchr(p,'\t');
142 874 : if (e == NULL) {
143 0 : DEBUG(SSSDBG_CRIT_FAILURE, "missing delimiter.\n");
144 0 : return EINVAL;
145 : } else {
146 874 : *e = '\0';
147 : }
148 874 : num = (uint32_t) strtoint32(p, &endptr, 10);
149 874 : error = errno;
150 874 : if (error != 0) {
151 0 : DEBUG(SSSDBG_CRIT_FAILURE,
152 : "strtol failed [%s].\n", strerror(error));
153 0 : return error;
154 : }
155 874 : 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 874 : *uid = num;
166 :
167 874 : return EOK;
168 :
169 : fail_fd:
170 0 : close(fd);
171 0 : return error;
172 : }
173 :
174 874 : 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 874 : errno = 0;
181 874 : num = strtol(name, &endptr, 10);
182 874 : error = errno;
183 874 : if (error == ERANGE) {
184 0 : perror("strtol");
185 0 : return error;
186 : }
187 :
188 874 : if (*endptr != '\0') {
189 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pid string contains extra characters.\n");
190 0 : return EINVAL;
191 : }
192 :
193 874 : if (num <= 0 || num >= INT_MAX) {
194 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pid out of range.\n");
195 0 : return ERANGE;
196 : }
197 :
198 874 : *pid = num;
199 :
200 874 : return EOK;
201 : }
202 :
203 1228 : static int only_numbers(char *p)
204 : {
205 1228 : while(*p!='\0' && isdigit(*p)) ++p;
206 1228 : return *p;
207 : }
208 :
209 6 : static errno_t get_active_uid_linux(hash_table_t *table, uid_t search_uid)
210 : {
211 6 : DIR *proc_dir = NULL;
212 : struct dirent *dirent;
213 : int ret, err;
214 6 : pid_t pid = -1;
215 : uid_t uid;
216 :
217 : hash_key_t key;
218 : hash_value_t value;
219 :
220 6 : proc_dir = opendir("/proc");
221 6 : 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 6 : errno = 0;
228 1238 : while ((dirent = readdir(proc_dir)) != NULL) {
229 1228 : if (only_numbers(dirent->d_name) != 0) continue;
230 874 : ret = name_to_pid(dirent->d_name, &pid);
231 874 : if (ret != EOK) {
232 0 : DEBUG(SSSDBG_CRIT_FAILURE, "name_to_pid failed.\n");
233 0 : goto done;
234 : }
235 :
236 874 : ret = get_uid_from_pid(pid, &uid);
237 874 : if (ret != EOK) {
238 0 : DEBUG(SSSDBG_CRIT_FAILURE, "get_uid_from_pid failed.\n");
239 0 : goto done;
240 : }
241 :
242 874 : if (table != NULL) {
243 309 : key.type = HASH_KEY_ULONG;
244 309 : key.ul = (unsigned long) uid;
245 309 : value.type = HASH_VALUE_ULONG;
246 309 : value.ul = (unsigned long) uid;
247 :
248 309 : ret = hash_enter(table, &key, &value);
249 309 : 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 565 : if (uid == search_uid) {
257 2 : ret = EOK;
258 2 : goto done;
259 : }
260 : }
261 :
262 :
263 872 : 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 6 : if (proc_dir != NULL) {
285 2 : err = closedir(proc_dir);
286 2 : if (err) {
287 0 : DEBUG(SSSDBG_CRIT_FAILURE, "closedir failed, bad dirp?\n");
288 : }
289 : }
290 6 : 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 : ret = sd_uid_get_sessions(uid, 0, NULL);
319 : if (ret > 0) {
320 : *result = true;
321 : return EOK;
322 : }
323 : if (ret == 0) {
324 : *result = false;
325 : }
326 : if (ret < 0) {
327 : 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 4 : ret = get_active_uid_linux(NULL, uid);
334 4 : if (ret != EOK && ret != ENOENT) {
335 0 : DEBUG(SSSDBG_CRIT_FAILURE, "get_uid_table failed.\n");
336 0 : return ret;
337 : }
338 :
339 4 : if (ret == EOK) {
340 2 : *result = true;
341 : } else {
342 2 : *result = false;
343 : }
344 :
345 4 : return EOK;
346 : }
|