ldb: make ldb a top level library for Samba 4.0
[ira/wip.git] / 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 Lesser 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 Lesser 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         _ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx, NULL);
49         if (_ldb_nss_ctx->ldb == NULL) {
50                 goto failed;
51         }
52
53         ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL);
54         if (ret != LDB_SUCCESS) {
55                 goto failed;
56         }
57
58         _ldb_nss_ctx->base = ldb_dn_new(_ldb_nss_ctx, _ldb_nss_ctx->ldb, _LDB_NSS_BASEDN);
59         if ( ! ldb_dn_validate(_ldb_nss_ctx->base)) {
60                 goto failed;
61         }
62
63         _ldb_nss_ctx->pw_cur = 0;
64         _ldb_nss_ctx->pw_res = NULL;
65         _ldb_nss_ctx->gr_cur = 0;
66         _ldb_nss_ctx->gr_res = NULL;
67
68         return NSS_STATUS_SUCCESS;
69
70 failed:
71         /* talloc_free(_ldb_nss_ctx); */
72         _ldb_nss_ctx = NULL;
73         return NSS_STATUS_UNAVAIL;
74 }
75
76 NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
77                                 char *buffer,
78                                 int buflen,
79                                 int *errnop,
80                                 struct ldb_message *msg)
81 {
82         int len;
83         int bufpos;
84         const char *tmp;
85
86         bufpos = 0;
87
88         /* get username */
89         tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL);
90         if (tmp == NULL) {
91                 /* this is a fatal error */
92                 *errnop = errno = ENOENT;
93                 return NSS_STATUS_UNAVAIL;
94         }
95         len = strlen(tmp)+1;
96         if (bufpos + len > buflen) {
97                 /* buffer too small */
98                 *errnop = errno = EAGAIN;
99                 return NSS_STATUS_TRYAGAIN;
100         }
101         memcpy(&buffer[bufpos], tmp, len);
102         result->pw_name = &buffer[bufpos];
103         bufpos += len;
104
105         /* get userPassword */
106         tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL);
107         if (tmp == NULL) {
108                 tmp = "LDB";
109         }
110         len = strlen(tmp)+1;
111         if (bufpos + len > buflen) {
112                 /* buffer too small */
113                 *errnop = errno = EAGAIN;
114                 return NSS_STATUS_TRYAGAIN;
115         }
116         memcpy(&buffer[bufpos], tmp, len);
117         result->pw_passwd = &buffer[bufpos];
118         bufpos += len;
119
120         /* this backend never serves an uid 0 user */
121         result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0);
122         if (result->pw_uid == 0) {
123                 /* this is a fatal error */
124                 *errnop = errno = ENOENT;
125                 return NSS_STATUS_UNAVAIL;
126         }
127
128         result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0);
129         if (result->pw_gid == 0) {
130                 /* this is a fatal error */
131                 *errnop = errno = ENOENT;
132                 return NSS_STATUS_UNAVAIL;
133         }
134
135         /* get gecos */
136         tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL);
137         if (tmp == NULL) {
138                 tmp = "";
139         }
140         len = strlen(tmp)+1;
141         if (bufpos + len > buflen) {
142                 /* buffer too small */
143                 *errnop = errno = EAGAIN;
144                 return NSS_STATUS_TRYAGAIN;
145         }
146         memcpy(&buffer[bufpos], tmp, len);
147         result->pw_gecos = &buffer[bufpos];
148         bufpos += len;
149
150         /* get homeDirectory */
151         tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL);
152         if (tmp == NULL) {
153                 tmp = "";
154         }
155         len = strlen(tmp)+1;
156         if (bufpos + len > buflen) {
157                 /* buffer too small */
158                 *errnop = errno = EAGAIN;
159                 return NSS_STATUS_TRYAGAIN;
160         }
161         memcpy(&buffer[bufpos], tmp, len);
162         result->pw_dir = &buffer[bufpos];
163         bufpos += len;
164
165         /* get shell */
166         tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL);
167         if (tmp == NULL) {
168                 tmp = "";
169         }
170         len = strlen(tmp)+1;
171         if (bufpos + len > buflen) {
172                 /* buffer too small */
173                 *errnop = errno = EAGAIN;
174                 return NSS_STATUS_TRYAGAIN;
175         }
176         memcpy(&buffer[bufpos], tmp, len);
177         result->pw_shell = &buffer[bufpos];
178         bufpos += len;
179
180         return NSS_STATUS_SUCCESS;
181 }
182
183 NSS_STATUS _ldb_nss_fill_group(struct group *result,
184                                 char *buffer,
185                                 int buflen,
186                                 int *errnop,
187                                 struct ldb_message *group,
188                                 struct ldb_result *members)
189 {
190         const char *tmp;
191         size_t len;
192         size_t bufpos;
193         size_t lsize;
194         unsigned int i;
195
196         bufpos = 0;
197
198         /* get group name */
199         tmp = ldb_msg_find_attr_as_string(group, "cn", NULL);
200         if (tmp == NULL) {
201                 /* this is a fatal error */
202                 *errnop = errno = ENOENT;
203                 return NSS_STATUS_UNAVAIL;
204         }
205         len = strlen(tmp)+1;
206         if (bufpos + len > buflen) {
207                 /* buffer too small */
208                 *errnop = errno = EAGAIN;
209                 return NSS_STATUS_TRYAGAIN;
210         }
211         memcpy(&buffer[bufpos], tmp, len);
212         result->gr_name = &buffer[bufpos];
213         bufpos += len;
214
215         /* get userPassword */
216         tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL);
217         if (tmp == NULL) {
218                 tmp = "LDB";
219         }
220         len = strlen(tmp)+1;
221         if (bufpos + len > buflen) {
222                 /* buffer too small */
223                 *errnop = errno = EAGAIN;
224                 return NSS_STATUS_TRYAGAIN;
225         }
226         memcpy(&buffer[bufpos], tmp, len);
227         result->gr_passwd = &buffer[bufpos];
228         bufpos += len;
229
230         result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0);
231         if (result->gr_gid == 0) {
232                 /* this is a fatal error */
233                 *errnop = errno = ENOENT;
234                 return NSS_STATUS_UNAVAIL;
235         }
236
237         /* check if there is enough memory for the list of pointers */
238         lsize = (members->count + 1) * sizeof(char *);
239
240         /* align buffer on pointer boundary */
241         bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*)));
242         if ((buflen - bufpos) < lsize) {
243                 /* buffer too small */
244                 *errnop = errno = EAGAIN;
245                 return NSS_STATUS_TRYAGAIN;
246         } 
247
248         result->gr_mem = (char **)&buffer[bufpos];
249         bufpos += lsize;
250
251         for (i = 0; i < members->count; i++) {
252                 tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL);
253                 if (tmp == NULL) {
254                         /* this is a fatal error */
255                         *errnop = errno = ENOENT;
256                         return NSS_STATUS_UNAVAIL;
257                 }
258                 len = strlen(tmp)+1;
259                 if (bufpos + len > buflen) {
260                         /* buffer too small */
261                         *errnop = errno = EAGAIN;
262                         return NSS_STATUS_TRYAGAIN;
263                 }
264                 memcpy(&buffer[bufpos], tmp, len);
265                 result->gr_mem[i] = &buffer[bufpos];
266                 bufpos += len;
267         }
268
269         result->gr_mem[i] = NULL;
270
271         return NSS_STATUS_SUCCESS;
272 }
273
274 NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
275                                 long int limit,
276                                 long int *start,
277                                 long int *size,
278                                 gid_t **groups,
279                                 int *errnop,
280                                 struct ldb_result *grlist)
281 {
282         NSS_STATUS ret;
283         unsigned int i;
284
285         for (i = 0; i < grlist->count; i++) {
286
287                 if (limit && (*start > limit)) {
288                         /* TODO: warn no all groups were reported */
289                         *errnop = 0;
290                         ret = NSS_STATUS_SUCCESS;
291                         goto done;
292                 }
293
294                 if (*start == *size) {
295                         /* buffer full, enlarge it */
296                         long int gs;
297                         gid_t *gm;
298
299                         gs = (*size) + 32;
300                         if (limit && (gs > limit)) {
301                                 gs = limit;
302                         }
303
304                         gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t));
305                         if ( ! gm) {
306                                 *errnop = ENOMEM;
307                                 ret = NSS_STATUS_UNAVAIL;
308                                 goto done;
309                         }
310
311                         *groups = gm;
312                         *size = gs;
313                 }
314
315                 (*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0);
316                 if ((*groups)[*start] == 0 || (*groups)[*start] == group) {
317                         /* skip root group or primary group */
318                         continue;
319                 }
320                 (*start)++;
321
322         }
323
324         *errnop = 0;
325         ret = NSS_STATUS_SUCCESS;
326 done:
327         return ret;
328 }
329
330 #define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0)
331
332 NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res,
333                                         struct ldb_dn *group_dn,
334                                         const char * const *attrs,
335                                         const char *mattr)
336 {
337         struct ldb_control **ctrls;
338         struct ldb_control *ctrl;
339         struct ldb_asq_control *asqc;
340         struct ldb_request *req;
341         int ret;
342         struct ldb_result *res = *_res;
343
344         ctrls = talloc_array(res, struct ldb_control *, 2);
345         _LDB_NSS_ALLOC_CHECK(ctrls);
346
347         ctrl = talloc(ctrls, struct ldb_control);
348         _LDB_NSS_ALLOC_CHECK(ctrl);
349
350         asqc = talloc(ctrl, struct ldb_asq_control);
351         _LDB_NSS_ALLOC_CHECK(asqc);
352
353         asqc->source_attribute = talloc_strdup(asqc, mattr);
354         _LDB_NSS_ALLOC_CHECK(asqc->source_attribute);
355
356         asqc->request = 1;
357         asqc->src_attr_len = strlen(asqc->source_attribute);
358         ctrl->oid = LDB_CONTROL_ASQ_OID;
359         ctrl->critical = 1;
360         ctrl->data = asqc;
361         ctrls[0] = ctrl;
362         ctrls[1] = NULL;
363
364         ret = ldb_build_search_req(
365                                 &req,
366                                 _ldb_nss_ctx->ldb,
367                                 res,
368                                 group_dn,
369                                 LDB_SCOPE_BASE,
370                                 "(objectClass=*)",
371                                 attrs,
372                                 ctrls,
373                                 res,
374                                 ldb_search_default_callback);
375         
376         if (ret != LDB_SUCCESS) {
377                 errno = ENOENT;
378                 return NSS_STATUS_UNAVAIL;
379         }
380
381         ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0);
382
383         ret = ldb_request(_ldb_nss_ctx->ldb, req);
384
385         if (ret == LDB_SUCCESS) {
386                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
387         } else {
388                 talloc_free(req);
389                 return NSS_STATUS_UNAVAIL;
390         }
391
392         talloc_free(req);
393         return NSS_STATUS_SUCCESS;
394 }
395