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   	
39   	#include "dhash.h"
40   	#include "util/util.h"
41   	
42   	#define INITIAL_TABLE_SIZE 64
43   	#define PATHLEN (NAME_MAX + 14)
44   	#define BUFSIZE 4096
45   	
46   	static void *hash_talloc(const size_t size, void *pvt)
47   	{
48   	    return talloc_size(pvt, size);
49   	}
50   	
51   	static void hash_talloc_free(void *ptr, void *pvt)
52   	{
53   	    talloc_free(ptr);
54   	}
55   	
56   	static errno_t get_uid_from_pid(const pid_t pid, uid_t *uid)
57   	{
58   	    int ret;
59   	    char path[PATHLEN];
60   	    struct stat stat_buf;
61   	    int fd;
62   	    char buf[BUFSIZE];
63   	    char *p;
64   	    char *e;
65   	    char *endptr;
66   	    long num=0;
67   	
68   	    ret = snprintf(path, PATHLEN, "/proc/%d/status", pid);
69   	    if (ret < 0) {
70   	        DEBUG(1, ("snprintf failed"));
71   	        return EINVAL;
72   	    } else if (ret >= PATHLEN) {
73   	        DEBUG(1, ("path too long?!?!\n"));
74   	        return EINVAL;
75   	    }
76   	
77   	    ret = lstat(path, &stat_buf);
78   	    if (ret == -1) {
79   	        if (errno == ENOENT) {
80   	            DEBUG(7, ("Proc file [%s] is not available anymore, continuing.\n",
81   	                      path));
82   	            return EOK;
83   	        }
84   	        DEBUG(1, ("lstat failed [%d][%s].\n", errno, strerror(errno)));
85   	        return errno;
86   	    }
87   	
88   	    if (!S_ISREG(stat_buf.st_mode)) {
89   	        DEBUG(1, ("not a regular file\n"));
90   	        return EINVAL;
91   	    }
92   	
93   	    fd = open(path, O_RDONLY);
94   	    if (fd == -1) {
95   	        if (errno == ENOENT) {
96   	            DEBUG(7, ("Proc file [%s] is not available anymore, continuing.\n",
97   	                      path));
98   	            return EOK;
99   	        }
100  	        DEBUG(1, ("open failed [%d][%s].\n", errno, strerror(errno)));
101  	        return errno;
102  	    }
103  	    ret = read(fd, buf, BUFSIZE);
104  	    if (ret == -1) {
105  	        DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno)));
106  	        return errno;
107  	    }
108  	
109  	    ret = close(fd);
110  	    if (ret == -1) {
111  	        DEBUG(1, ("close failed [%d][%s].\n", errno, strerror(errno)));
112  	    }
113  	
114  	    p = strstr(buf, "\nUid:\t");
115  	    if (p != NULL) {
116  	        p += 6;
117  	        e = strchr(p,'\t');
118  	        if (e == NULL) {
119  	            DEBUG(1, ("missing delimiter.\n"));
120  	            return EINVAL;
121  	        } else {
122  	            *e = '\0';
123  	        }
124  	        num = strtol(p, &endptr, 10);
125  	        if(errno == ERANGE) {
126  	            DEBUG(1, ("strtol failed [%s].\n", strerror(errno)));
127  	            return errno;
128  	        }
129  	        if (*endptr != '\0') {
130  	            DEBUG(1, ("uid contains extra characters\n"));
131  	            return EINVAL;
132  	        }
133  	
134  	        if (num < 0 || num >= INT_MAX) {
135  	            DEBUG(1, ("uid out of range.\n"));
136  	            return ERANGE;
137  	        }
138  	
139  	    } else {
140  	        DEBUG(1, ("format error\n"));
141  	        return EINVAL;
142  	    }
143  	
144  	    *uid = num;
145  	
146  	    return EOK;
147  	}
148  	
149  	static errno_t name_to_pid(const char *name, pid_t *pid)
150  	{
151  	    long num;
152  	    char *endptr;
153  	
154  	    errno = 0;
155  	    num = strtol(name, &endptr, 10);
156  	    if(errno == ERANGE) {
157  	        perror("strtol");
158  	        return errno;
159  	    }
160  	
161  	    if (*endptr != '\0') {
162  	        DEBUG(1, ("pid string contains extra characters.\n"));
163  	        return EINVAL;
164  	    }
165  	
166  	    if (num <= 0 || num >= INT_MAX) {
167  	        DEBUG(1, ("pid out of range.\n"));
168  	        return ERANGE;
169  	    }
170  	
171  	    *pid = num;
172  	
173  	    return EOK;
174  	}
175  	
Event example: Example where the function is checked.
Also see events: [check_return][example][example][example][unchecked_value]
176  	static int only_numbers(char *p)
177  	{
178  	    while(*p!='\0' && isdigit(*p)) ++p;
179  	    return *p;
180  	}
181  	
182  	static errno_t get_active_uid_linux(hash_table_t *table, uid_t search_uid)
183  	{
184  	    DIR *proc_dir = NULL;
185  	    struct dirent *dirent;
186  	    int ret;
187  	    pid_t pid = -1;
188  	    uid_t uid;
189  	
190  	    hash_key_t key;
191  	    hash_value_t value;
192  	
193  	    proc_dir = opendir("/proc");
194  	    if (proc_dir == NULL) {
195  	        DEBUG(1, ("Cannot open proc dir.\n"));
196  	        ret = errno;
197  	        goto done;
198  	    };
199  	
200  	    errno = 0;
201  	    while ((dirent = readdir(proc_dir)) != NULL) {
202  	        if (only_numbers(dirent->d_name) != 0) continue;
203  	        ret = name_to_pid(dirent->d_name, &pid);
204  	        if (ret != EOK) {
205  	            DEBUG(1, ("name_to_pid failed.\n"));
206  	            goto done;
207  	        }
208  	
209  	        ret = get_uid_from_pid(pid, &uid);
210  	        if (ret != EOK) {
211  	            DEBUG(1, ("get_uid_from_pid failed.\n"));
212  	            goto done;
213  	        }
214  	
215  	        if (table != NULL) {
216  	            key.type = HASH_KEY_ULONG;
217  	            key.ul = (unsigned long) uid;
218  	            value.type = HASH_VALUE_ULONG;
219  	            value.ul = (unsigned long) uid;
220  	
221  	            ret = hash_enter(table, &key, &value);
222  	            if (ret != HASH_SUCCESS) {
223  	                DEBUG(1, ("cannot add to table [%s]\n", hash_error_string(ret)));
224  	                ret = ENOMEM;
225  	                goto done;
226  	            }
227  	        } else {
228  	            if (uid == search_uid) {
229  	                ret = EOK;
230  	                goto done;
231  	            }
232  	        }
233  	
234  	
235  	        errno = 0;
236  	    }
237  	    if (errno != 0 && dirent == NULL) {
238  	        DEBUG(1 ,("readdir failed.\n"));
239  	        ret = errno;
240  	        goto done;
241  	    }
242  	
Event example: Example where the function is checked.
Also see events: [check_return][example][example][example][unchecked_value]
243  	    ret = closedir(proc_dir);
244  	    proc_dir = NULL;
245  	    if (ret == -1) {
246  	        DEBUG(1 ,("closedir failed, watch out.\n"));
247  	    }
248  	
249  	    if (table != NULL) {
250  	        ret = EOK;
251  	    } else {
252  	        ret = ENOENT;
253  	    }
254  	
255  	done:
Event check_return: Calling function "closedir" without checking return value.
Event unchecked_value: No check of the return value of "closedir(proc_dir)".
Also see events: [example][example][example][example]
256  	    if (proc_dir != NULL) closedir(proc_dir);
257  	    return ret;
258  	}
259  	
260  	errno_t get_uid_table(TALLOC_CTX *mem_ctx, hash_table_t **table)
261  	{
262  	#ifdef __linux__
263  	    int ret;
264  	
265  	    ret = hash_create_ex(INITIAL_TABLE_SIZE, table, 0, 0, 0, 0,
266  	                         hash_talloc, hash_talloc_free, mem_ctx,
267  	                         NULL, NULL);
268  	    if (ret != HASH_SUCCESS) {
269  	        DEBUG(1, ("hash_create_ex failed [%s]\n", hash_error_string(ret)));
270  	        return ENOMEM;
271  	    }
272  	
273  	    return get_active_uid_linux(*table, 0);
274  	#else
275  	    return ENOSYS;
276  	#endif
277  	}
278  	
279  	errno_t check_if_uid_is_active(uid_t uid, bool *result)
280  	{
281  	    int ret;
282  	
283  	    ret = get_active_uid_linux(NULL, uid);
284  	    if (ret != EOK && ret != ENOENT) {
285  	        DEBUG(1, ("get_uid_table failed.\n"));
286  	        return ret;
287  	    }
288  	
289  	    if (ret == EOK) {
290  	        *result = true;
291  	    } else {
292  	        *result = false;
293  	    }
294  	
295  	    return EOK;
296  	}