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   	/* PASSWD 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_getpwent_data {
33   	    size_t len;
34   	    size_t ptr;
35   	    uint8_t *data;
36   	} sss_nss_getpwent_data;
37   	
38   	static void sss_nss_getpwent_data_clean(void) {
39   	
40   	    if (sss_nss_getpwent_data.data != NULL) {
41   	        free(sss_nss_getpwent_data.data);
42   	        sss_nss_getpwent_data.data = NULL;
43   	    }
44   	    sss_nss_getpwent_data.len = 0;
45   	    sss_nss_getpwent_data.ptr = 0;
46   	}
47   	
48   	/* GETPWNAM Request:
49   	 *
50   	 * 0-X: string with name
51   	 *
52   	 * GERTPWUID Request:
53   	 *
54   	 * 0-3: 32bit number with uid
55   	 *
56   	 * Replies:
57   	 *
58   	 * 0-3: 32bit unsigned number of results
59   	 * 4-7: 32bit unsigned (reserved/padding)
60   	 * For each result:
61   	 *  0-3: 32bit number uid
62   	 *  4-7: 32bit number gid
63   	 *  8-X: sequence of 5, 0 terminated, strings (name, passwd, gecos, dir, shell)
64   	 */
65   	
66   	struct sss_nss_pw_rep {
67   	    struct passwd *result;
68   	    char *buffer;
69   	    size_t buflen;
70   	};
71   	
72   	static int sss_nss_getpw_readrep(struct sss_nss_pw_rep *pr,
73   	                                 uint8_t *buf, size_t *len)
74   	{
75   	    size_t i, slen, dlen;
76   	    char *sbuf;
77   	    uint32_t c;
78   	
79   	    if (*len < 13) { /* not enough space for data, bad packet */
80   	        return EBADMSG;
81   	    }
82   	
83   	    SAFEALIGN_COPY_UINT32(&c, buf, NULL);
84   	    pr->result->pw_uid = c;
85   	    SAFEALIGN_COPY_UINT32(&c, buf+sizeof(uint32_t), NULL);
86   	    pr->result->pw_gid = c;
87   	
88   	    sbuf = (char *)&buf[8];
89   	    slen = *len - 8;
90   	    dlen = pr->buflen;
91   	
92   	    i = 0;
93   	    pr->result->pw_name = &(pr->buffer[i]);
94   	    while (slen > i && dlen > 0) {
95   	        pr->buffer[i] = sbuf[i];
96   	        if (pr->buffer[i] == '\0') break;
97   	        i++;
98   	        dlen--;
99   	    }
100  	    if (slen <= i) { /* premature end of buf */
101  	        return EBADMSG;
102  	    }
103  	    if (dlen <= 0) { /* not enough memory */
104  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
105  	    }
106  	    i++;
107  	    dlen--;
108  	
109  	    pr->result->pw_passwd = &(pr->buffer[i]);
110  	    while (slen > i && dlen > 0) {
111  	        pr->buffer[i] = sbuf[i];
112  	        if (pr->buffer[i] == '\0') break;
113  	        i++;
114  	        dlen--;
115  	    }
116  	    if (slen <= i) { /* premature end of buf */
117  	        return EBADMSG;
118  	    }
119  	    if (dlen <= 0) { /* not enough memory */
120  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
121  	    }
122  	    i++;
123  	    dlen--;
124  	
125  	    pr->result->pw_gecos = &(pr->buffer[i]);
126  	    while (slen > i && dlen > 0) {
127  	        pr->buffer[i] = sbuf[i];
128  	        if (pr->buffer[i] == '\0') break;
129  	        i++;
130  	        dlen--;
131  	    }
132  	    if (slen <= i) { /* premature end of buf */
133  	        return EBADMSG;
134  	    }
135  	    if (dlen <= 0) { /* not enough memory */
136  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
137  	    }
138  	    i++;
139  	    dlen--;
140  	
141  	    pr->result->pw_dir = &(pr->buffer[i]);
142  	    while (slen > i && dlen > 0) {
143  	        pr->buffer[i] = sbuf[i];
144  	        if (pr->buffer[i] == '\0') break;
145  	        i++;
146  	        dlen--;
147  	    }
148  	    if (slen <= i) { /* premature end of buf */
149  	        return EBADMSG;
150  	    }
151  	    if (dlen <= 0) { /* not enough memory */
152  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
153  	    }
154  	    i++;
155  	    dlen--;
156  	
157  	    pr->result->pw_shell = &(pr->buffer[i]);
158  	    while (slen > i && dlen > 0) {
159  	        pr->buffer[i] = sbuf[i];
160  	        if (pr->buffer[i] == '\0') break;
161  	        i++;
162  	        dlen--;
163  	    }
164  	    if (slen <= i) { /* premature end of buf */
165  	        return EBADMSG;
166  	    }
167  	    if (dlen <= 0) { /* not enough memory */
168  	        return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
169  	    }
170  	
171  	    *len = slen -i -1;
172  	
173  	    return 0;
174  	}
175  	
176  	enum nss_status _nss_sss_getpwnam_r(const char *name, struct passwd *result,
177  	                                    char *buffer, size_t buflen, int *errnop)
178  	{
179  	    struct sss_cli_req_data rd;
180  	    struct sss_nss_pw_rep pwrep;
181  	    uint8_t *repbuf;
182  	    size_t replen, len;
183  	    enum nss_status nret;
184  	    int ret;
185  	
186  	    /* Caught once glibc passing in buffer == 0x0 */
187  	    if (!buffer || !buflen) return ERANGE;
188  	
189  	    rd.len = strlen(name) + 1;
190  	    rd.data = name;
191  	
192  	    nret = sss_nss_make_request(SSS_NSS_GETPWNAM, &rd,
193  	                                &repbuf, &replen, errnop);
194  	    if (nret != NSS_STATUS_SUCCESS) {
195  	        return nret;
196  	    }
197  	
198  	    pwrep.result = result;
199  	    pwrep.buffer = buffer;
200  	    pwrep.buflen = buflen;
201  	
202  	    /* no results if not found */
203  	    if (((uint32_t *)repbuf)[0] == 0) {
204  	        free(repbuf);
205  	        return NSS_STATUS_NOTFOUND;
206  	    }
207  	
208  	    /* only 1 result is accepted for this function */
209  	    if (((uint32_t *)repbuf)[0] != 1) {
210  	        *errnop = EBADMSG;
211  	        return NSS_STATUS_TRYAGAIN;
212  	    }
213  	
214  	    len = replen - 8;
215  	    ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len);
216  	    free(repbuf);
217  	    if (ret) {
218  	        *errnop = ret;
219  	        return NSS_STATUS_TRYAGAIN;
220  	    }
221  	
222  	    return NSS_STATUS_SUCCESS;
223  	}
224  	
225  	enum nss_status _nss_sss_getpwuid_r(uid_t uid, struct passwd *result,
226  	                                    char *buffer, size_t buflen, int *errnop)
227  	{
228  	    struct sss_cli_req_data rd;
229  	    struct sss_nss_pw_rep pwrep;
230  	    uint8_t *repbuf;
231  	    size_t replen, len;
232  	    enum nss_status nret;
233  	    uint32_t user_uid;
234  	    int ret;
235  	
236  	    /* Caught once glibc passing in buffer == 0x0 */
237  	    if (!buffer || !buflen) return ERANGE;
238  	
239  	    user_uid = uid;
240  	    rd.len = sizeof(uint32_t);
241  	    rd.data = &user_uid;
242  	
Event alloc_arg: Calling allocation function "sss_nss_make_request" on "repbuf". [model]
Also see events: [leaked_storage]
243  	    nret = sss_nss_make_request(SSS_NSS_GETPWUID, &rd,
244  	                                &repbuf, &replen, errnop);
At conditional (1): "nret != 1": Taking false branch.
245  	    if (nret != NSS_STATUS_SUCCESS) {
246  	        return nret;
247  	    }
248  	
249  	    pwrep.result = result;
250  	    pwrep.buffer = buffer;
251  	    pwrep.buflen = buflen;
252  	
253  	    /* no results if not found */
At conditional (2): "(uint32_t*)repbuf[0] == 0U": Taking false branch.
254  	    if (((uint32_t *)repbuf)[0] == 0) {
255  	        free(repbuf);
256  	        return NSS_STATUS_NOTFOUND;
257  	    }
258  	
259  	    /* only 1 result is accepted for this function */
At conditional (3): "(uint32_t*)repbuf[0] != 1U": Taking true branch.
260  	    if (((uint32_t *)repbuf)[0] != 1) {
261  	        *errnop = EBADMSG;
Event leaked_storage: Variable "repbuf" going out of scope leaks the storage it points to.
Also see events: [alloc_arg]
262  	        return NSS_STATUS_TRYAGAIN;
263  	    }
264  	
265  	    len = replen - 8;
266  	    ret = sss_nss_getpw_readrep(&pwrep, repbuf+8, &len);
267  	    free(repbuf);
268  	    if (ret) {
269  	        *errnop = ret;
270  	        return NSS_STATUS_TRYAGAIN;
271  	    }
272  	
273  	    return NSS_STATUS_SUCCESS;
274  	}
275  	
276  	enum nss_status _nss_sss_setpwent(void)
277  	{
278  	    enum nss_status nret;
279  	    int errnop;
280  	
281  	    /* make sure we do not have leftovers, and release memory */
282  	    sss_nss_getpwent_data_clean();
283  	
284  	    nret = sss_nss_make_request(SSS_NSS_SETPWENT,
285  	                                NULL, NULL, NULL, &errnop);
286  	    if (nret != NSS_STATUS_SUCCESS) {
287  	        errno = errnop;
288  	        return nret;
289  	    }
290  	
291  	    return NSS_STATUS_SUCCESS;
292  	}
293  	
294  	enum nss_status _nss_sss_getpwent_r(struct passwd *result,
295  	                                    char *buffer, size_t buflen,
296  	                                    int *errnop)
297  	{
298  	    struct sss_cli_req_data rd;
299  	    struct sss_nss_pw_rep pwrep;
300  	    uint8_t *repbuf;
301  	    size_t replen;
302  	    enum nss_status nret;
303  	    uint32_t num_entries;
304  	    int ret;
305  	
306  	    /* Caught once glibc passing in buffer == 0x0 */
307  	    if (!buffer || !buflen) return ERANGE;
308  	
309  	    /* if there are leftovers return the next one */
310  	    if (sss_nss_getpwent_data.data != NULL &&
311  	        sss_nss_getpwent_data.ptr < sss_nss_getpwent_data.len) {
312  	
313  	        repbuf = sss_nss_getpwent_data.data + sss_nss_getpwent_data.ptr;
314  	        replen = sss_nss_getpwent_data.len - sss_nss_getpwent_data.ptr;
315  	
316  	        pwrep.result = result;
317  	        pwrep.buffer = buffer;
318  	        pwrep.buflen = buflen;
319  	
320  	        ret = sss_nss_getpw_readrep(&pwrep, repbuf, &replen);
321  	        if (ret) {
322  	            *errnop = ret;
323  	            return NSS_STATUS_TRYAGAIN;
324  	        }
325  	
326  	        /* advance buffer pointer */
327  	        sss_nss_getpwent_data.ptr = sss_nss_getpwent_data.len - replen;
328  	
329  	        return NSS_STATUS_SUCCESS;
330  	    }
331  	
332  	    /* release memory if any */
333  	    sss_nss_getpwent_data_clean();
334  	
335  	    /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
336  	    num_entries = SSS_NSS_MAX_ENTRIES;
337  	    rd.len = sizeof(uint32_t);
338  	    rd.data = &num_entries;
339  	
340  	    nret = sss_nss_make_request(SSS_NSS_GETPWENT, &rd,
341  	                                &repbuf, &replen, errnop);
342  	    if (nret != NSS_STATUS_SUCCESS) {
343  	        return nret;
344  	    }
345  	
346  	    /* no results if not found */
347  	    if ((((uint32_t *)repbuf)[0] == 0) || (replen - 8 == 0)) {
348  	        free(repbuf);
349  	        return NSS_STATUS_NOTFOUND;
350  	    }
351  	
352  	    sss_nss_getpwent_data.data = repbuf;
353  	    sss_nss_getpwent_data.len = replen;
354  	    sss_nss_getpwent_data.ptr = 8; /* skip metadata fields */
355  	
356  	    /* call again ourselves, this will return the first result */
357  	    return _nss_sss_getpwent_r(result, buffer, buflen, errnop);
358  	}
359  	
360  	enum nss_status _nss_sss_endpwent(void)
361  	{
362  	    enum nss_status nret;
363  	    int errnop;
364  	
365  	    /* make sure we do not have leftovers, and release memory */
366  	    sss_nss_getpwent_data_clean();
367  	
368  	    nret = sss_nss_make_request(SSS_NSS_ENDPWENT,
369  	                                NULL, NULL, NULL, &errnop);
370  	    if (nret != NSS_STATUS_SUCCESS) {
371  	        errno = errnop;
372  	        return nret;
373  	    }
374  	
375  	    return NSS_STATUS_SUCCESS;
376  	}