chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / nis / nss_nisplus / nisplus-grp.c
1 /* Copyright (C) 1997, 2001, 2002, 2003, 2005, 2006
2    Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
5
6    The GNU C 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 2.1 of the License, or (at your option) any later version.
10
11    The GNU C 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    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 the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <atomic.h>
22 #include <nss.h>
23 #include <grp.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <bits/libc-lock.h>
28 #include <rpcsvc/nis.h>
29
30 #include "nss-nisplus.h"
31 #include "nisplus-parser.h"
32 #include <libnsl.h>
33 #include <nis_intern.h>
34 #include <nis_xdr.h>
35
36
37 __libc_lock_define_initialized (static, lock);
38
39 /* Connection information.  */
40 static ib_request *ibreq;
41 static directory_obj *dir;
42 static dir_binding bptr;
43 static char *tablepath;
44 static char *tableptr;
45 /* Cursor.  */
46 static netobj cursor;
47
48
49 nis_name grp_tablename_val attribute_hidden;
50 size_t grp_tablename_len attribute_hidden;
51
52 enum nss_status
53 _nss_grp_create_tablename (int *errnop)
54 {
55   if (grp_tablename_val == NULL)
56     {
57       const char *local_dir = nis_local_directory ();
58       size_t local_dir_len = strlen (local_dir);
59       static const char prefix[] = "group.org_dir.";
60
61       char *p = malloc (sizeof (prefix) + local_dir_len);
62       if (p == NULL)
63         {
64           *errnop = errno;
65           return NSS_STATUS_TRYAGAIN;
66         }
67
68       memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
69
70       grp_tablename_len = sizeof (prefix) - 1 + local_dir_len;
71
72       atomic_write_barrier ();
73
74       if (atomic_compare_and_exchange_bool_acq (&grp_tablename_val, p, NULL))
75         {
76           /* Another thread already installed the value.  */
77           free (p);
78           grp_tablename_len = strlen (grp_tablename_val);
79         }
80     }
81
82   return NSS_STATUS_SUCCESS;
83 }
84
85
86 static void
87 internal_endgrent (void)
88 {
89   __nisbind_destroy (&bptr);
90   memset (&bptr, '\0', sizeof (bptr));
91
92   nis_free_directory (dir);
93   dir = NULL;
94
95   nis_free_request (ibreq);
96   ibreq = NULL;
97
98   xdr_free ((xdrproc_t) xdr_netobj, (char *) &cursor);
99   memset (&cursor, '\0', sizeof (cursor));
100
101   free (tablepath);
102   tableptr = tablepath = NULL;
103 }
104
105
106 static enum nss_status
107 internal_setgrent (int *errnop)
108 {
109   enum nss_status status = NSS_STATUS_SUCCESS;
110
111   if (grp_tablename_val == NULL)
112     status = _nss_grp_create_tablename (errnop);
113
114   if (status == NSS_STATUS_SUCCESS)
115     {
116       ibreq = __create_ib_request (grp_tablename_val, 0);
117       if (ibreq == NULL)
118         {
119           *errnop = errno;
120           return NSS_STATUS_TRYAGAIN;
121         }
122
123       nis_error retcode = __prepare_niscall (grp_tablename_val, &dir, &bptr, 0);
124       if (retcode != NIS_SUCCESS)
125         {
126           nis_free_request (ibreq);
127           ibreq = NULL;
128           status = niserr2nss (retcode);
129         }
130     }
131
132   return status;
133 }
134
135
136 enum nss_status
137 _nss_nisplus_setgrent (int stayopen)
138 {
139   enum nss_status status;
140
141   __libc_lock_lock (lock);
142
143   internal_endgrent ();
144
145   // XXX We need to be able to set errno.  Pass in new parameter.
146   int err;
147   status = internal_setgrent (&err);
148
149   __libc_lock_unlock (lock);
150
151   return status;
152 }
153
154
155 enum nss_status
156 _nss_nisplus_endgrent (void)
157 {
158   __libc_lock_lock (lock);
159
160   internal_endgrent ();
161
162   __libc_lock_unlock (lock);
163
164   return NSS_STATUS_SUCCESS;
165 }
166
167
168 static enum nss_status
169 internal_nisplus_getgrent_r (struct group *gr, char *buffer, size_t buflen,
170                              int *errnop)
171 {
172   int parse_res = -1;
173   enum nss_status retval = NSS_STATUS_SUCCESS;
174
175   /* Get the next entry until we found a correct one. */
176   do
177     {
178       nis_error status;
179       nis_result result;
180       memset (&result, '\0', sizeof (result));
181
182       if (cursor.n_bytes == NULL)
183         {
184           if (ibreq == NULL)
185             {
186               retval = internal_setgrent (errnop);
187               if (retval != NSS_STATUS_SUCCESS)
188                 return retval;
189             }
190
191           status = __do_niscall3 (&bptr, NIS_IBFIRST,
192                                   (xdrproc_t) _xdr_ib_request,
193                                   (caddr_t) ibreq,
194                                   (xdrproc_t) _xdr_nis_result,
195                                   (caddr_t) &result,
196                                   0, NULL);
197         }
198       else
199         {
200           ibreq->ibr_cookie.n_bytes = cursor.n_bytes;
201           ibreq->ibr_cookie.n_len = cursor.n_len;
202
203           status = __do_niscall3 (&bptr, NIS_IBNEXT,
204                                   (xdrproc_t) _xdr_ib_request,
205                                   (caddr_t) ibreq,
206                                   (xdrproc_t) _xdr_nis_result,
207                                   (caddr_t) &result,
208                                   0, NULL);
209
210           ibreq->ibr_cookie.n_bytes = NULL;
211           ibreq->ibr_cookie.n_len = 0;
212         }
213
214       if (status != NIS_SUCCESS)
215         return niserr2nss (status);
216
217       if (NIS_RES_STATUS (&result) == NIS_NOTFOUND)
218         {
219           /* No more entries on this server.  This means we have to go
220              to the next server on the path.  */
221           status = __follow_path (&tablepath, &tableptr, ibreq, &bptr);
222           if (status != NIS_SUCCESS)
223             return niserr2nss (status);
224
225           directory_obj *newdir = NULL;
226           dir_binding newbptr;
227           status = __prepare_niscall (ibreq->ibr_name, &newdir, &newbptr, 0);
228           if (status != NIS_SUCCESS)
229             return niserr2nss (status);
230
231           nis_free_directory (dir);
232           dir = newdir;
233           __nisbind_destroy (&bptr);
234           bptr = newbptr;
235
236           xdr_free ((xdrproc_t) xdr_netobj, (char *) &result.cookie);
237           result.cookie.n_bytes = NULL;
238           result.cookie.n_len = 0;
239           parse_res = 0;
240           goto next;
241         }
242       else if (NIS_RES_STATUS (&result) != NIS_SUCCESS)
243         return niserr2nss (NIS_RES_STATUS (&result));
244
245       parse_res = _nss_nisplus_parse_grent (&result, gr,
246                                             buffer, buflen, errnop);
247       if (__builtin_expect (parse_res == -1, 0))
248         {
249           *errnop = ERANGE;
250           retval = NSS_STATUS_TRYAGAIN;
251           goto freeres;
252         }
253
254     next:
255       /* Free the old cursor.  */
256       xdr_free ((xdrproc_t) xdr_netobj, (char *) &cursor);
257       /* Remember the new one.  */
258       cursor.n_bytes = result.cookie.n_bytes;
259       cursor.n_len = result.cookie.n_len;
260       /* Free the result structure.  NB: we do not remove the cookie.  */
261       result.cookie.n_bytes = NULL;
262       result.cookie.n_len = 0;
263     freeres:
264       xdr_free ((xdrproc_t) _xdr_nis_result, (char *) &result);
265       memset (&result, '\0', sizeof (result));
266     }
267   while (!parse_res);
268
269   return retval;
270 }
271
272 enum nss_status
273 _nss_nisplus_getgrent_r (struct group *result, char *buffer, size_t buflen,
274                          int *errnop)
275 {
276   int status;
277
278   __libc_lock_lock (lock);
279
280   status = internal_nisplus_getgrent_r (result, buffer, buflen, errnop);
281
282   __libc_lock_unlock (lock);
283
284   return status;
285 }
286
287 enum nss_status
288 _nss_nisplus_getgrnam_r (const char *name, struct group *gr,
289                          char *buffer, size_t buflen, int *errnop)
290 {
291   int parse_res;
292
293   if (grp_tablename_val == NULL)
294     {
295       enum nss_status status = _nss_grp_create_tablename (errnop);
296
297       if (status != NSS_STATUS_SUCCESS)
298         return status;
299     }
300
301   if (name == NULL)
302     {
303       *errnop = EINVAL;
304       return NSS_STATUS_NOTFOUND;
305     }
306
307   nis_result *result;
308   char buf[strlen (name) + 9 + grp_tablename_len];
309   int olderr = errno;
310
311   snprintf (buf, sizeof (buf), "[name=%s],%s", name, grp_tablename_val);
312
313   result = nis_list (buf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
314
315   if (result == NULL)
316     {
317       *errnop = ENOMEM;
318       return NSS_STATUS_TRYAGAIN;
319     }
320
321   if (__builtin_expect (niserr2nss (result->status) != NSS_STATUS_SUCCESS, 0))
322     {
323       enum nss_status status = niserr2nss (result->status);
324
325       nis_freeresult (result);
326       return status;
327     }
328
329   parse_res = _nss_nisplus_parse_grent (result, gr, buffer, buflen, errnop);
330   nis_freeresult (result);
331   if (__builtin_expect (parse_res < 1, 0))
332     {
333       if (parse_res == -1)
334         {
335           *errnop = ERANGE;
336           return NSS_STATUS_TRYAGAIN;
337         }
338       else
339         {
340           __set_errno (olderr);
341           return NSS_STATUS_NOTFOUND;
342         }
343     }
344
345   return NSS_STATUS_SUCCESS;
346 }
347
348 enum nss_status
349 _nss_nisplus_getgrgid_r (const gid_t gid, struct group *gr,
350                          char *buffer, size_t buflen, int *errnop)
351 {
352   if (grp_tablename_val == NULL)
353     {
354       enum nss_status status = _nss_grp_create_tablename (errnop);
355
356       if (status != NSS_STATUS_SUCCESS)
357         return status;
358     }
359
360   int parse_res;
361   nis_result *result;
362   char buf[8 + 3 * sizeof (unsigned long int) + grp_tablename_len];
363   int olderr = errno;
364
365   snprintf (buf, sizeof (buf), "[gid=%lu],%s",
366             (unsigned long int) gid, grp_tablename_val);
367
368   result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
369
370   if (result == NULL)
371     {
372       *errnop = ENOMEM;
373       return NSS_STATUS_TRYAGAIN;
374     }
375
376   if (__builtin_expect (niserr2nss (result->status) != NSS_STATUS_SUCCESS, 0))
377     {
378       enum nss_status status = niserr2nss (result->status);
379
380       __set_errno (olderr);
381
382       nis_freeresult (result);
383       return status;
384     }
385
386   parse_res = _nss_nisplus_parse_grent (result, gr, buffer, buflen, errnop);
387
388   nis_freeresult (result);
389   if (__builtin_expect (parse_res < 1, 0))
390     {
391       __set_errno (olderr);
392
393       if (parse_res == -1)
394         {
395           *errnop = ERANGE;
396           return NSS_STATUS_TRYAGAIN;
397         }
398       else
399         return NSS_STATUS_NOTFOUND;
400     }
401
402   return NSS_STATUS_SUCCESS;
403 }