fc1ac2e9d71737ec32dc8242861439a6139a9c68
[platform/upstream/glibc.git] / nis / nss_nisplus / nisplus-proto.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 <ctype.h>
21 #include <errno.h>
22 #include <netdb.h>
23 #include <nss.h>
24 #include <string.h>
25 #include <rpcsvc/nis.h>
26 #include <libc-lock.h>
27
28 #include "nss-nisplus.h"
29
30 __libc_lock_define_initialized (static, lock)
31
32 static nis_result *result;
33 static nis_name tablename_val;
34 static u_long tablename_len;
35
36 #define NISENTRYVAL(idx, col, res) \
37         (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_val)
38
39 #define NISENTRYLEN(idx, col, res) \
40         (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_len)
41
42
43 static int
44 _nss_nisplus_parse_protoent (nis_result *result, struct protoent *proto,
45                              char *buffer, size_t buflen, int *errnop)
46 {
47   char *first_unused = buffer;
48   size_t room_left = buflen;
49   unsigned int i;
50
51   if (result == NULL)
52     return 0;
53
54   if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
55       || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
56       || strcmp (NIS_RES_OBJECT (result)->EN_data.en_type,
57                  "protocols_tbl") != 0
58       || NIS_RES_OBJECT (result)->EN_data.en_cols.en_cols_len < 3)
59     return 0;
60
61   /* Generate the protocols entry format and use the normal parser */
62   if (NISENTRYLEN (0, 0, result) + 1 > room_left)
63     {
64     no_more_room:
65       *errnop = ERANGE;
66       return -1;
67     }
68   strncpy (first_unused, NISENTRYVAL (0, 0, result),
69            NISENTRYLEN (0, 0, result));
70   first_unused[NISENTRYLEN (0, 0, result)] = '\0';
71   proto->p_name = first_unused;
72   size_t len = strlen (first_unused) + 1;
73   room_left -= len;
74   first_unused += len;
75
76
77   proto->p_proto = atoi (NISENTRYVAL (0, 2, result));
78
79   /* XXX Rewrite at some point to allocate the array first and then
80      copy the strings.  It wasteful to first concatenate the strings
81      to just split them again later.  */
82   char *line = first_unused;
83   for (i = 0; i < NIS_RES_NUMOBJ (result); ++i)
84     {
85       if (strcmp (NISENTRYVAL (i, 1, result), proto->p_name) != 0)
86         {
87           if (NISENTRYLEN (i, 1, result) + 2 > room_left)
88             goto no_more_room;
89           *first_unused++ = ' ';
90           first_unused = __stpncpy (first_unused, NISENTRYVAL (i, 1, result),
91                                     NISENTRYLEN (i, 1, result));
92           room_left -= NISENTRYLEN (i, 1, result) + 1;
93         }
94     }
95   *first_unused++ = '\0';
96
97   /* Adjust the pointer so it is aligned for
98      storing pointers.  */
99   size_t adjust = ((__alignof__ (char *)
100                     - (first_unused - (char *) 0) % __alignof__ (char *))
101                    % __alignof__ (char *));
102   if (room_left < adjust + sizeof (char *))
103     goto no_more_room;
104   first_unused += adjust;
105   room_left -= adjust;
106   proto->p_aliases = (char **) first_unused;
107
108   /* For the terminating NULL pointer.  */
109   room_left -= sizeof (char *);
110
111   i = 0;
112   while (*line != '\0')
113     {
114       /* Skip leading blanks.  */
115       while (isspace (*line))
116         line++;
117       if (*line == '\0')
118         break;
119
120       if (room_left < sizeof (char *))
121         goto no_more_room;
122
123       room_left -= sizeof (char *);
124       proto->p_aliases[i++] = line;
125
126       while (*line != '\0' && *line != ' ')
127         ++line;
128
129       if (*line == ' ')
130         *line++ = '\0';
131     }
132   proto->p_aliases[i] = NULL;
133
134   return 1;
135 }
136
137 static enum nss_status
138 _nss_create_tablename (int *errnop)
139 {
140   if (tablename_val == NULL)
141     {
142       const char *local_dir = nis_local_directory ();
143       size_t local_dir_len = strlen (local_dir);
144       static const char prefix[] = "protocols.org_dir.";
145
146       char *p = malloc (sizeof (prefix) + local_dir_len);
147       if (p == NULL)
148         {
149           *errnop = errno;
150           return NSS_STATUS_TRYAGAIN;
151         }
152
153       memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
154
155       tablename_len = sizeof (prefix) - 1 + local_dir_len;
156
157       atomic_write_barrier ();
158
159       tablename_val = p;
160     }
161
162   return NSS_STATUS_SUCCESS;
163 }
164
165 enum nss_status
166 _nss_nisplus_setprotoent (int stayopen)
167 {
168   enum nss_status status = NSS_STATUS_SUCCESS;
169
170   __libc_lock_lock (lock);
171
172   if (result != NULL)
173     {
174       nis_freeresult (result);
175       result = NULL;
176     }
177
178   if (tablename_val == NULL)
179     {
180       int err;
181       status = _nss_create_tablename (&err);
182     }
183
184   __libc_lock_unlock (lock);
185
186   return status;
187 }
188
189 enum nss_status
190 _nss_nisplus_endprotoent (void)
191 {
192   __libc_lock_lock (lock);
193
194   if (result != NULL)
195     {
196       nis_freeresult (result);
197       result = NULL;
198     }
199
200   __libc_lock_unlock (lock);
201
202   return NSS_STATUS_SUCCESS;
203 }
204
205 static enum nss_status
206 internal_nisplus_getprotoent_r (struct protoent *proto, char *buffer,
207                                 size_t buflen, int *errnop)
208 {
209   int parse_res;
210
211   /* Get the next entry until we found a correct one. */
212   do
213     {
214       nis_result *saved_res;
215
216       if (result == NULL)
217         {
218           saved_res = NULL;
219           if (tablename_val == NULL)
220             {
221               enum nss_status status = _nss_create_tablename (errnop);
222
223               if (status != NSS_STATUS_SUCCESS)
224                 return status;
225             }
226
227           result = nis_first_entry (tablename_val);
228           if (result == NULL)
229             {
230               *errnop = errno;
231               return NSS_STATUS_TRYAGAIN;
232             }
233           if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
234             return niserr2nss (result->status);
235         }
236       else
237         {
238           saved_res = result;
239           result = nis_next_entry (tablename_val, &result->cookie);
240           if (result == NULL)
241             {
242               *errnop = errno;
243               return NSS_STATUS_TRYAGAIN;
244             }
245           if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
246             {
247               nis_freeresult (saved_res);
248               return niserr2nss (result->status);
249             }
250         }
251
252       parse_res = _nss_nisplus_parse_protoent (result, proto, buffer,
253                                                buflen, errnop);
254       if (parse_res == -1)
255         {
256           nis_freeresult (result);
257           result = saved_res;
258           *errnop = ERANGE;
259           return NSS_STATUS_TRYAGAIN;
260         }
261       else
262         {
263           if (saved_res)
264             nis_freeresult (saved_res);
265         }
266     }
267   while (!parse_res);
268
269   return NSS_STATUS_SUCCESS;
270 }
271
272 enum nss_status
273 _nss_nisplus_getprotoent_r (struct protoent *result, char *buffer,
274                             size_t buflen, int *errnop)
275 {
276   int status;
277
278   __libc_lock_lock (lock);
279
280   status = internal_nisplus_getprotoent_r (result, buffer, buflen, errnop);
281
282   __libc_lock_unlock (lock);
283
284   return status;
285 }
286
287 enum nss_status
288 _nss_nisplus_getprotobyname_r (const char *name, struct protoent *proto,
289                                char *buffer, size_t buflen, int *errnop)
290 {
291   int parse_res;
292
293   if (tablename_val == NULL)
294     {
295       __libc_lock_lock (lock);
296
297       enum nss_status status = _nss_create_tablename (errnop);
298
299       __libc_lock_unlock (lock);
300
301       if (status != NSS_STATUS_SUCCESS)
302         return status;
303     }
304
305   if (name == NULL)
306     return NSS_STATUS_NOTFOUND;
307
308   char buf[strlen (name) + 10 + tablename_len];
309   int olderr = errno;
310
311   /* Search at first in the alias list, and use the correct name
312      for the next search */
313   snprintf (buf, sizeof (buf), "[name=%s],%s", name, tablename_val);
314   nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
315
316   if (result != NULL)
317     {
318       char *bufptr = buf;
319
320       /* If we did not find it, try it as original name. But if the
321          database is correct, we should find it in the first case, too */
322       if ((result->status != NIS_SUCCESS
323            && result->status != NIS_S_SUCCESS)
324           || __type_of (result->objects.objects_val) != NIS_ENTRY_OBJ
325           || strcmp (result->objects.objects_val->EN_data.en_type,
326                          "protocols_tbl") != 0
327           || result->objects.objects_val->EN_data.en_cols.en_cols_len < 3)
328         snprintf (buf, sizeof (buf), "[cname=%s],%s", name, tablename_val);
329       else
330         {
331           /* We need to allocate a new buffer since there is no
332              guarantee the returned name has a length limit.  */
333           const char *entryval = NISENTRYVAL (0, 0, result);
334           size_t buflen = strlen (entryval) + 10 + tablename_len;
335           bufptr = alloca (buflen);
336           snprintf (bufptr, buflen, "[cname=%s],%s",
337                     entryval, tablename_val);
338         }
339
340       nis_freeresult (result);
341       result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL);
342     }
343
344   if (result == NULL)
345     {
346       __set_errno (ENOMEM);
347       return NSS_STATUS_TRYAGAIN;
348     }
349
350   if (__glibc_unlikely (niserr2nss (result->status) != NSS_STATUS_SUCCESS))
351     {
352       enum nss_status status = niserr2nss (result->status);
353
354       __set_errno (olderr);
355
356       nis_freeresult (result);
357       return status;
358     }
359
360   parse_res = _nss_nisplus_parse_protoent (result, proto, buffer, buflen,
361                                            errnop);
362
363   nis_freeresult (result);
364
365   if (parse_res < 1)
366     {
367       if (parse_res == -1)
368         {
369           *errnop = ERANGE;
370           return NSS_STATUS_TRYAGAIN;
371         }
372       else
373         {
374           __set_errno (olderr);
375           return NSS_STATUS_NOTFOUND;
376         }
377     }
378
379   return NSS_STATUS_SUCCESS;
380 }
381
382 enum nss_status
383 _nss_nisplus_getprotobynumber_r (const int number, struct protoent *proto,
384                                  char *buffer, size_t buflen, int *errnop)
385 {
386   if (tablename_val == NULL)
387     {
388       __libc_lock_lock (lock);
389
390       enum nss_status status = _nss_create_tablename (errnop);
391
392       __libc_lock_unlock (lock);
393
394       if (status != NSS_STATUS_SUCCESS)
395         return status;
396     }
397
398   char buf[12 + 3 * sizeof (number) + tablename_len];
399   int olderr = errno;
400
401   snprintf (buf, sizeof (buf), "[number=%d],%s", number, tablename_val);
402
403   nis_result *result = nis_list (buf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
404
405   if (result == NULL)
406     {
407       __set_errno (ENOMEM);
408       return NSS_STATUS_TRYAGAIN;
409     }
410
411   if (__glibc_unlikely (niserr2nss (result->status) != NSS_STATUS_SUCCESS))
412     {
413       enum nss_status status = niserr2nss (result->status);
414
415       __set_errno (olderr);
416
417       nis_freeresult (result);
418       return status;
419     }
420
421   int parse_res = _nss_nisplus_parse_protoent (result, proto, buffer, buflen,
422                                                errnop);
423
424   nis_freeresult (result);
425
426   if (parse_res < 1)
427     {
428       if (parse_res == -1)
429         {
430           *errnop = ERANGE;
431           return NSS_STATUS_TRYAGAIN;
432         }
433       else
434         {
435           __set_errno (olderr);
436           return NSS_STATUS_NOTFOUND;
437         }
438     }
439
440   return NSS_STATUS_SUCCESS;
441 }