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