1    	/*
2    	 * System Security Services Daemon. NSS client interface
3    	 *
4    	 * Copyright (C) Simo Sorce 2007
5    	 *
6    	 * This program is free software; you can redistribute it and/or modify
7    	 * it under the terms of the GNU Lesser General Public License as
8    	 * published by the Free Software Foundation; either version 2.1 of the
9    	 * License, or (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 Lesser General Public License for more details.
15   	 *
16   	 * You should have received a copy of the GNU Lesser General Public
17   	 * License along with this program; if not, write to the Free Software
18   	 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19   	 */
20   	
21   	/* GROUP database NSS interface */
22   	
23   	#include <nss.h>
24   	#include <errno.h>
25   	#include <sys/types.h>
26   	#include <unistd.h>
27   	#include <stdlib.h>
28   	#include <stdint.h>
29   	#include <string.h>
30   	#include "sss_cli.h"
31   	
32   	static struct sss_nss_getgrent_data {
33   	    size_t len;
34   	    size_t ptr;
35   	    uint8_t *data;
36   	} sss_nss_getgrent_data;
37   	
38   	static void sss_nss_getgrent_data_clean(void) {
39   	
40   	    if (sss_nss_getgrent_data.data != NULL) {
41   	        free(sss_nss_getgrent_data.data);
42   	        sss_nss_getgrent_data.data = NULL;
43   	    }
44   	    sss_nss_getgrent_data.len = 0;
45   	    sss_nss_getgrent_data.ptr = 0;
46   	}
47   	
48   	/* GETGRNAM Request:
49   	 *
50   	 * 0-X: string with name
51   	 *
52   	 * GERTGRGID Request:
53   	 *
54   	 * 0-7: 32bit number with gid
55   	 *
56   	 * INITGROUPS Request:
57   	 *
58   	 * 0-3: 32bit number with gid
59   	 * 4-7: 32bit unsigned with max num of entries
60   	 *
61   	 * Replies:
62   	 *
63   	 * 0-3: 32bit unsigned number of results
64   	 * 4-7: 32bit unsigned (reserved/padding)
65   	 *  For each result (64bit padded ?):
66   	 *  0-3: 32bit number gid
67   	 *  4-7: 32bit unsigned number of members
68   	 *  8-X: sequence of 0 terminated strings (name, passwd, mem..)
69   	 *
70   	 *  FIXME: do we need to pad so that each result is 32 bit aligned ?
71   	 */
72   	struct sss_nss_gr_rep {
73   	    struct group *result;
74   	    char *buffer;
75   	    size_t buflen;
76   	};
77   	
78   	static int sss_nss_getgr_readrep(struct sss_nss_gr_rep *pr,
79   	                                 uint8_t *buf, size_t *len)
80   	{
81   	    size_t i, l, slen, ptmem, pad;
82   	    ssize_t dlen;
83   	    char *sbuf;
84   	    uint32_t mem_num;
85   	    uint32_t c;
86   	
87   	    if (*len < 11) { /* not enough space for data, bad packet */
88   	        return EBADMSG;
89   	    }
90   	
91   	    SAFEALIGN_COPY_UINT32(&c, buf, NULL);
92   	    pr->result->gr_gid = c;
93   	    SAFEALIGN_COPY_UINT32(&mem_num, buf+sizeof(uint32_t), NULL);
94   	
95   	    sbuf = (char *)&buf[8];
96   	    slen = *len - 8;
97   	    dlen = pr->buflen;
98   	
99   	    pr->result->gr_name = &(pr->buffer[0]);
100  	    i = 0;
101  	    while (slen > i && dlen > 0) {
102  	        pr->buffer[i] = sbuf[i];
103  	        if (pr->buffer[i] == '\0') break;
104  	        i++;
105  	        dlen--;
106  	    }
107  	    if (slen <= i) { /* premature end of buf */
108  	        return EBADMSG;
109  	    }
110  	    if (dlen <= 0) { /* not enough memory */
111  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
112  	    }
113  	    i++;
114  	    dlen--;
115  	
116  	    pr->result->gr_passwd = &(pr->buffer[i]);
117  	    while (slen > i && dlen > 0) {
118  	        pr->buffer[i] = sbuf[i];
119  	        if (pr->buffer[i] == '\0') break;
120  	        i++;
121  	        dlen--;
122  	    }
123  	    if (slen <= i) { /* premature end of buf */
124  	        return EBADMSG;
125  	    }
126  	    if (dlen <= 0) { /* not enough memory */
127  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
128  	    }
129  	    i++;
130  	    dlen--;
131  	
132  	    /* Make sure pr->buffer[i+pad] is 32 bit aligned */
133  	    pad = 0;
134  	    while((i + pad) % 4) {
135  	        pad++;
136  	    }
137  	
138  	    /* now members */
139  	    pr->result->gr_mem = (char **)&(pr->buffer[i+pad]);
140  	    ptmem = (sizeof(char *) * (mem_num + 1)) + pad;
141  	    if (ptmem > dlen) {
142  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
143  	    }
144  	    dlen -= ptmem;
145  	    ptmem += i;
146  	    pr->result->gr_mem[mem_num] = NULL; /* terminate array */
147  	
148  	    for (l = 0; l < mem_num; l++) {
149  	        pr->result->gr_mem[l] = &(pr->buffer[ptmem]);
150  	        while ((slen > i) && (dlen > 0)) {
151  	            pr->buffer[ptmem] = sbuf[i];
152  	            if (pr->buffer[ptmem] == '\0') break;
153  	            i++;
154  	            dlen--;
155  	            ptmem++;
156  	        }
157  	        if (slen <= i) { /* premature end of buf */
158  	            return EBADMSG;
159  	        }
160  	        if (dlen <= 0) { /* not enough memory */
161  	            return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
162  	        }
163  	        i++;
164  	        dlen--;
165  	        ptmem++;
166  	    }
167  	
168  	    *len = slen -i;
169  	    return 0;
170  	}
171  	
172  	/* INITGROUP Reply:
173  	 *
174  	 * 0-3: 32bit unsigned number of results
175  	 * 4-7: 32bit unsigned (reserved/padding)
176  	 * For each result:
177  	 *  0-4: 32bit number with gid
178  	 */
179  	
180  	
181  	enum nss_status _nss_sss_initgroups_dyn(const char *user, gid_t group,
182  	                                        long int *start, long int *size,
183  	                                        gid_t **groups, long int limit,
184  	                                        int *errnop)
185  	{
186  	    struct sss_cli_req_data rd;
187  	    uint8_t *repbuf;
188  	    size_t replen;
189  	    enum nss_status nret;
190  	    uint32_t *rbuf;
191  	    uint32_t num_ret;
192  	    long int l, max_ret;
193  	
194  	    rd.len = strlen(user) +1;
195  	    rd.data = user;
196  	
197  	    nret = sss_nss_make_request(SSS_NSS_INITGR, &rd,
198  	                                &repbuf, &replen, errnop);
199  	    if (nret != NSS_STATUS_SUCCESS) {
200  	        return nret;
201  	    }
202  	
203  	    /* no results if not found */
204  	    num_ret = ((uint32_t *)repbuf)[0];
205  	    if (num_ret == 0) {
206  	        free(repbuf);
207  	        return NSS_STATUS_NOTFOUND;
208  	    }
209  	    max_ret = num_ret;
210  	
211  	    /* check we have enough space in the buffer */
212  	    if ((*size - *start) < num_ret) {
213  	        long int newsize;
214  	        gid_t *newgroups;
215  	
216  	        newsize = *size + num_ret;
217  	        if ((limit > 0) && (newsize > limit)) {
218  	            newsize = limit;
219  	            max_ret = newsize - *start;
220  	        }
221  	
222  	        newgroups = (gid_t *)realloc((*groups), newsize * sizeof(**groups));
223  	        if (!newgroups) {
224  	            *errnop = ENOMEM;
225  	            free(repbuf);
226  	            return NSS_STATUS_TRYAGAIN;
227  	        }
228  	        *groups = newgroups;
229  	        *size = newsize;
230  	    }
231  	
232  	    rbuf = &((uint32_t *)repbuf)[2];
233  	    for (l = 0; l < max_ret; l++) {
234  	        (*groups)[*start] = rbuf[l];
235  	        *start += 1;
236  	    }
237  	
238  	    return NSS_STATUS_SUCCESS;
239  	}
240  	
241  	
242  	enum nss_status _nss_sss_getgrnam_r(const char *name, struct group *result,
243  	                                    char *buffer, size_t buflen, int *errnop)
244  	{
245  	    struct sss_cli_req_data rd;
246  	    struct sss_nss_gr_rep grrep;
247  	    uint8_t *repbuf;
248  	    size_t replen, len;
249  	    enum nss_status nret;
250  	    int ret;
251  	
252  	    /* Caught once glibc passing in buffer == 0x0 */
253  	    if (!buffer || !buflen) return ERANGE;
254  	
255  	    rd.len = strlen(name) + 1;
256  	    rd.data = name;
257  	
258  	    nret = sss_nss_make_request(SSS_NSS_GETGRNAM, &rd,
259  	                                &repbuf, &replen, errnop);
260  	    if (nret != NSS_STATUS_SUCCESS) {
261  	        return nret;
262  	    }
263  	
264  	    grrep.result = result;
265  	    grrep.buffer = buffer;
266  	    grrep.buflen = buflen;
267  	
268  	    /* no results if not found */
269  	    if (((uint32_t *)repbuf)[0] == 0) {
270  	        free(repbuf);
271  	        return NSS_STATUS_NOTFOUND;
272  	    }
273  	
274  	    /* only 1 result is accepted for this function */
275  	    if (((uint32_t *)repbuf)[0] != 1) {
276  	        *errnop = EBADMSG;
277  	        return NSS_STATUS_TRYAGAIN;
278  	    }
279  	
280  	    len = replen - 8;
281  	    ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
282  	    free(repbuf);
283  	    if (ret) {
284  	        *errnop = ret;
285  	        return NSS_STATUS_TRYAGAIN;
286  	    }
287  	
288  	    return NSS_STATUS_SUCCESS;
289  	}
290  	
291  	enum nss_status _nss_sss_getgrgid_r(gid_t gid, struct group *result,
292  	                                    char *buffer, size_t buflen, int *errnop)
293  	{
294  	    struct sss_cli_req_data rd;
295  	    struct sss_nss_gr_rep grrep;
296  	    uint8_t *repbuf;
297  	    size_t replen, len;
298  	    enum nss_status nret;
299  	    uint32_t group_gid;
300  	    int ret;
301  	
302  	    /* Caught once glibc passing in buffer == 0x0 */
303  	    if (!buffer || !buflen) return ERANGE;
304  	
305  	    group_gid = gid;
306  	    rd.len = sizeof(uint32_t);
307  	    rd.data = &group_gid;
308  	
Event alloc_arg: Calling allocation function "sss_nss_make_request" on "repbuf". [model]
Also see events: [leaked_storage]
309  	    nret = sss_nss_make_request(SSS_NSS_GETGRGID, &rd,
310  	                                &repbuf, &replen, errnop);
At conditional (1): "nret != 1": Taking false branch.
311  	    if (nret != NSS_STATUS_SUCCESS) {
312  	        return nret;
313  	    }
314  	
315  	    grrep.result = result;
316  	    grrep.buffer = buffer;
317  	    grrep.buflen = buflen;
318  	
319  	    /* no results if not found */
At conditional (2): "(uint32_t*)repbuf[0] == 0U": Taking false branch.
320  	    if (((uint32_t *)repbuf)[0] == 0) {
321  	        free(repbuf);
322  	        return NSS_STATUS_NOTFOUND;
323  	    }
324  	
325  	    /* only 1 result is accepted for this function */
At conditional (3): "(uint32_t*)repbuf[0] != 1U": Taking true branch.
326  	    if (((uint32_t *)repbuf)[0] != 1) {
327  	        *errnop = EBADMSG;
Event leaked_storage: Variable "repbuf" going out of scope leaks the storage it points to.
Also see events: [alloc_arg]
328  	        return NSS_STATUS_TRYAGAIN;
329  	    }
330  	
331  	    len = replen - 8;
332  	    ret = sss_nss_getgr_readrep(&grrep, repbuf+8, &len);
333  	    free(repbuf);
334  	    if (ret) {
335  	        *errnop = ret;
336  	        return NSS_STATUS_TRYAGAIN;
337  	    }
338  	
339  	    return NSS_STATUS_SUCCESS;
340  	}
341  	
342  	enum nss_status _nss_sss_setgrent(void)
343  	{
344  	    enum nss_status nret;
345  	    int errnop;
346  	
347  	    /* make sure we do not have leftovers, and release memory */
348  	    sss_nss_getgrent_data_clean();
349  	
350  	    nret = sss_nss_make_request(SSS_NSS_SETGRENT,
351  	                                NULL, NULL, NULL, &errnop);
352  	    if (nret != NSS_STATUS_SUCCESS) {
353  	        errno = errnop;
354  	        return nret;
355  	    }
356  	
357  	    return NSS_STATUS_SUCCESS;
358  	}
359  	
360  	enum nss_status _nss_sss_getgrent_r(struct group *result,
361  	                                    char *buffer, size_t buflen, int *errnop)
362  	{
363  	    struct sss_cli_req_data rd;
364  	    struct sss_nss_gr_rep grrep;
365  	    uint8_t *repbuf;
366  	    size_t replen;
367  	    enum nss_status nret;
368  	    uint32_t num_entries;
369  	    int ret;
370  	
371  	    /* Caught once glibc passing in buffer == 0x0 */
372  	    if (!buffer || !buflen) return ERANGE;
373  	
374  	    /* if there are leftovers return the next one */
375  	    if (sss_nss_getgrent_data.data != NULL &&
376  	        sss_nss_getgrent_data.ptr < sss_nss_getgrent_data.len) {
377  	
378  	        repbuf = (uint8_t *)sss_nss_getgrent_data.data +
379  	                    sss_nss_getgrent_data.ptr;
380  	        replen = sss_nss_getgrent_data.len -
381  	                    sss_nss_getgrent_data.ptr;
382  	
383  	        grrep.result = result;
384  	        grrep.buffer = buffer;
385  	        grrep.buflen = buflen;
386  	
387  	        ret = sss_nss_getgr_readrep(&grrep, repbuf, &replen);
388  	        if (ret) {
389  	            *errnop = ret;
390  	            return NSS_STATUS_TRYAGAIN;
391  	        }
392  	
393  	        /* advance buffer pointer */
394  	        sss_nss_getgrent_data.ptr = sss_nss_getgrent_data.len - replen;
395  	
396  	        return NSS_STATUS_SUCCESS;
397  	    }
398  	
399  	    /* release memory if any */
400  	    sss_nss_getgrent_data_clean();
401  	
402  	    /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
403  	    num_entries = SSS_NSS_MAX_ENTRIES;
404  	    rd.len = sizeof(uint32_t);
405  	    rd.data = &num_entries;
406  	
407  	    nret = sss_nss_make_request(SSS_NSS_GETGRENT, &rd,
408  	                                &repbuf, &replen, errnop);
409  	    if (nret != NSS_STATUS_SUCCESS) {
410  	        return nret;
411  	    }
412  	
413  	    /* no results if not found */
414  	    if ((((uint32_t *)repbuf)[0] == 0) || (replen - 8 == 0)) {
415  	        free(repbuf);
416  	        return NSS_STATUS_NOTFOUND;
417  	    }
418  	
419  	    sss_nss_getgrent_data.data = repbuf;
420  	    sss_nss_getgrent_data.len = replen;
421  	    sss_nss_getgrent_data.ptr = 8; /* skip metadata fields */
422  	
423  	    /* call again ourselves, this will return the first result */
424  	    return _nss_sss_getgrent_r(result, buffer, buflen, errnop);
425  	}
426  	
427  	enum nss_status _nss_sss_endgrent(void)
428  	{
429  	    enum nss_status nret;
430  	    int errnop;
431  	
432  	    /* make sure we do not have leftovers, and release memory */
433  	    sss_nss_getgrent_data_clean();
434  	
435  	    nret = sss_nss_make_request(SSS_NSS_ENDGRENT,
436  	                                NULL, NULL, NULL, &errnop);
437  	    if (nret != NSS_STATUS_SUCCESS) {
438  	        errno = errnop;
439  	        return nret;
440  	    }
441  	
442  	    return NSS_STATUS_SUCCESS;
443  	}