r23794: convert more code from LGPLv2+ to LGPLv3+
[jra/samba/.git] / source3 / lib / ldb / nssldb / ldb-nss.c
1 /* 
2    LDB nsswitch module
3
4    Copyright (C) Simo Sorce 2006
5    
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public
8    License as published by the Free Software Foundation; either
9    version 3 of the License, or (at your option) any later version.
10    
11    This library 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 GNU
14    Library General Public License for more details.
15    
16    You should have received a copy of the GNU Library General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "ldb-nss.h"
21
22 struct _ldb_nss_context *_ldb_nss_ctx = NULL;
23
24 NSS_STATUS _ldb_nss_init(void)
25 {
26         int ret;
27
28         pid_t mypid = getpid();
29
30         if (_ldb_nss_ctx != NULL) {
31                 if (_ldb_nss_ctx->pid == mypid) {
32                         /* already initialized */
33                         return NSS_STATUS_SUCCESS;
34                 } else {
35                         /* we are in a forked child now, reinitialize */
36                         talloc_free(_ldb_nss_ctx);
37                         _ldb_nss_ctx = NULL;
38                 }
39         }
40                 
41         _ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid);
42         if (_ldb_nss_ctx == NULL) {
43                 return NSS_STATUS_UNAVAIL;
44         }
45
46         _ldb_nss_ctx->pid = mypid;
47
48         ret = ldb_global_init();
49         if (ret != 0) {
50                 goto failed;
51         }
52
53         _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx);
54         if (_ldb_nss_ctx->ldb == NULL) {
55                 goto failed;
56         }
57
58         ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL);
59         if (ret != LDB_SUCCESS) {
60                 goto failed;
61         }
62
63         _ldb_nss_ctx->base = ldb_dn_explode(_ldb_nss_ctx, _LDB_NSS_BASEDN);
64         if (_ldb_nss_ctx->base == NULL) {
65                 goto failed;
66         }
67
68         _ldb_nss_ctx->pw_cur = 0;
69         _ldb_nss_ctx->pw_res = NULL;
70         _ldb_nss_ctx->gr_cur = 0;
71         _ldb_nss_ctx->gr_res = NULL;
72
73         return NSS_STATUS_SUCCESS;
74
75 failed:
76         /* talloc_free(_ldb_nss_ctx); */
77         _ldb_nss_ctx = NULL;
78         return NSS_STATUS_UNAVAIL;
79 }
80
81 NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
82                                 char *buffer,
83                                 int buflen,
84                                 int *errnop,
85                                 struct ldb_message *msg)
86 {
87         int len;
88         int bufpos;
89         const char *tmp;
90
91         bufpos = 0;
92
93         /* get username */
94         tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL);
95         if (tmp == NULL) {
96                 /* this is a fatal error */
97                 *errnop = errno = ENOENT;
98                 return NSS_STATUS_UNAVAIL;
99         }
100         len = strlen(tmp)+1;
101         if (bufpos + len > buflen) {
102                 /* buffer too small */
103                 *errnop = errno = EAGAIN;
104                 return NSS_STATUS_TRYAGAIN;
105         }
106         memcpy(&buffer[bufpos], tmp, len);
107         result->pw_name = &buffer[bufpos];
108         bufpos += len;
109
110         /* get userPassword */
111         tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL);
112         if (tmp == NULL) {
113                 tmp = "LDB";
114         }
115         len = strlen(tmp)+1;
116         if (bufpos + len > buflen) {
117                 /* buffer too small */
118                 *errnop = errno = EAGAIN;
119                 return NSS_STATUS_TRYAGAIN;
120         }
121         memcpy(&buffer[bufpos], tmp, len);
122         result->pw_passwd = &buffer[bufpos];
123         bufpos += len;
124
125         /* this backend never serves an uid 0 user */
126         result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0);
127         if (result->pw_uid == 0) {
128                 /* this is a fatal error */
129                 *errnop = errno = ENOENT;
130                 return NSS_STATUS_UNAVAIL;
131         }
132
133         result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0);
134         if (result->pw_gid == 0) {
135                 /* this is a fatal error */
136                 *errnop = errno = ENOENT;
137                 return NSS_STATUS_UNAVAIL;
138         }
139
140         /* get gecos */
141         tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL);
142         if (tmp == NULL) {
143                 tmp = "";
144         }
145         len = strlen(tmp)+1;
146         if (bufpos + len > buflen) {
147                 /* buffer too small */
148                 *errnop = errno = EAGAIN;
149                 return NSS_STATUS_TRYAGAIN;
150         }
151         memcpy(&buffer[bufpos], tmp, len);
152         result->pw_gecos = &buffer[bufpos];
153         bufpos += len;
154
155         /* get homeDirectory */
156         tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL);
157         if (tmp == NULL) {
158                 tmp = "";
159         }
160         len = strlen(tmp)+1;
161         if (bufpos + len > buflen) {
162                 /* buffer too small */
163                 *errnop = errno = EAGAIN;
164                 return NSS_STATUS_TRYAGAIN;
165         }
166         memcpy(&buffer[bufpos], tmp, len);
167         result->pw_dir = &buffer[bufpos];
168         bufpos += len;
169
170         /* get shell */
171         tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL);
172         if (tmp == NULL) {
173                 tmp = "";
174         }
175         len = strlen(tmp)+1;
176         if (bufpos + len > buflen) {
177                 /* buffer too small */
178                 *errnop = errno = EAGAIN;
179                 return NSS_STATUS_TRYAGAIN;
180         }
181         memcpy(&buffer[bufpos], tmp, len);
182         result->pw_shell = &buffer[bufpos];
183         bufpos += len;
184
185         return NSS_STATUS_SUCCESS;
186 }
187
188 NSS_STATUS _ldb_nss_fill_group(struct group *result,
189                                 char *buffer,
190                                 int buflen,
191                                 int *errnop,
192                                 struct ldb_message *group,
193                                 struct ldb_result *members)
194 {
195         const char *tmp;
196         size_t len;
197         size_t bufpos;
198         size_t lsize;
199         int i;
200
201         bufpos = 0;
202
203         /* get group name */
204         tmp = ldb_msg_find_attr_as_string(group, "cn", NULL);
205         if (tmp == NULL) {
206                 /* this is a fatal error */
207                 *errnop = errno = ENOENT;
208                 return NSS_STATUS_UNAVAIL;
209         }
210         len = strlen(tmp)+1;
211         if (bufpos + len > buflen) {
212                 /* buffer too small */
213                 *errnop = errno = EAGAIN;
214                 return NSS_STATUS_TRYAGAIN;
215         }
216         memcpy(&buffer[bufpos], tmp, len);
217         result->gr_name = &buffer[bufpos];
218         bufpos += len;
219
220         /* get userPassword */
221         tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL);
222         if (tmp == NULL) {
223                 tmp = "LDB";
224         }
225         len = strlen(tmp)+1;
226         if (bufpos + len > buflen) {
227                 /* buffer too small */
228                 *errnop = errno = EAGAIN;
229                 return NSS_STATUS_TRYAGAIN;
230         }
231         memcpy(&buffer[bufpos], tmp, len);
232         result->gr_passwd = &buffer[bufpos];
233         bufpos += len;
234
235         result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0);
236         if (result->gr_gid == 0) {
237                 /* this is a fatal error */
238                 *errnop = errno = ENOENT;
239                 return NSS_STATUS_UNAVAIL;
240         }
241
242         /* check if there is enough memory for the list of pointers */
243         lsize = (members->count + 1) * sizeof(char *);
244
245         /* align buffer on pointer boundary */
246         bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*)));
247         if ((buflen - bufpos) < lsize) {
248                 /* buffer too small */
249                 *errnop = errno = EAGAIN;
250                 return NSS_STATUS_TRYAGAIN;
251         } 
252
253         result->gr_mem = (char **)&buffer[bufpos];
254         bufpos += lsize;
255
256         for (i = 0; i < members->count; i++) {
257                 tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL);
258                 if (tmp == NULL) {
259                         /* this is a fatal error */
260                         *errnop = errno = ENOENT;
261                         return NSS_STATUS_UNAVAIL;
262                 }
263                 len = strlen(tmp)+1;
264                 if (bufpos + len > buflen) {
265                         /* buffer too small */
266                         *errnop = errno = EAGAIN;
267                         return NSS_STATUS_TRYAGAIN;
268                 }
269                 memcpy(&buffer[bufpos], tmp, len);
270                 result->gr_mem[i] = &buffer[bufpos];
271                 bufpos += len;
272         }
273
274         result->gr_mem[i] = NULL;
275
276         return NSS_STATUS_SUCCESS;
277 }
278
279 NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
280                                 long int limit,
281                                 long int *start,
282                                 long int *size,
283                                 gid_t **groups,
284                                 int *errnop,
285                                 struct ldb_result *grlist)
286 {
287         NSS_STATUS ret;
288         int i;
289
290         for (i = 0; i < grlist->count; i++) {
291
292                 if (limit && (*start > limit)) {
293                         /* TODO: warn no all groups were reported */
294                         *errnop = 0;
295                         ret = NSS_STATUS_SUCCESS;
296                         goto done;
297                 }
298
299                 if (*start == *size) {
300                         /* buffer full, enlarge it */
301                         long int gs;
302                         gid_t *gm;
303
304                         gs = (*size) + 32;
305                         if (limit && (gs > limit)) {
306                                 gs = limit;
307                         }
308
309                         gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t));
310                         if ( ! gm) {
311                                 *errnop = ENOMEM;
312                                 ret = NSS_STATUS_UNAVAIL;
313                                 goto done;
314                         }
315
316                         *groups = gm;
317                         *size = gs;
318                 }
319
320                 (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0);
321                 if ((*groups)[*start] == 0 || (*groups)[*start] == group) {
322                         /* skip root group or primary group */
323                         continue;
324                 }
325                 (*start)++;
326
327         }
328
329         *errnop = 0;
330         ret = NSS_STATUS_SUCCESS;
331 done:
332         return ret;
333 }
334
335 #define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0)
336
337 NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res,
338                                         struct ldb_dn *group_dn,
339                                         const char * const *attrs,
340                                         const char *mattr)
341 {
342         struct ldb_control **ctrls;
343         struct ldb_control *ctrl;
344         struct ldb_asq_control *asqc;
345         struct ldb_request *req;
346         int ret;
347         struct ldb_result *res = *_res;
348
349         ctrls = talloc_array(res, struct ldb_control *, 2);
350         _LDB_NSS_ALLOC_CHECK(ctrls);
351
352         ctrl = talloc(ctrls, struct ldb_control);
353         _LDB_NSS_ALLOC_CHECK(ctrl);
354
355         asqc = talloc(ctrl, struct ldb_asq_control);
356         _LDB_NSS_ALLOC_CHECK(asqc);
357
358         asqc->source_attribute = talloc_strdup(asqc, mattr);
359         _LDB_NSS_ALLOC_CHECK(asqc->source_attribute);
360
361         asqc->request = 1;
362         asqc->src_attr_len = strlen(asqc->source_attribute);
363         ctrl->oid = LDB_CONTROL_ASQ_OID;
364         ctrl->critical = 1;
365         ctrl->data = asqc;
366         ctrls[0] = ctrl;
367         ctrls[1] = NULL;
368
369         ret = ldb_build_search_req(
370                                 &req,
371                                 _ldb_nss_ctx->ldb,
372                                 res,
373                                 group_dn,
374                                 LDB_SCOPE_BASE,
375                                 "(objectClass=*)",
376                                 attrs,
377                                 ctrls,
378                                 res,
379                                 ldb_search_default_callback);
380         
381         if (ret != LDB_SUCCESS) {
382                 errno = ENOENT;
383                 return NSS_STATUS_UNAVAIL;
384         }
385
386         ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0);
387
388         ret = ldb_request(_ldb_nss_ctx->ldb, req);
389
390         if (ret == LDB_SUCCESS) {
391                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
392         } else {
393                 talloc_free(req);
394                 return NSS_STATUS_UNAVAIL;
395         }
396
397         talloc_free(req);
398         return NSS_STATUS_SUCCESS;
399 }
400