1 /* Parse a service line from nsswitch.conf.
2    Copyright (c) 1996-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
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 <nsswitch.h>
20 
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdbool.h>
24 
25 /* Staging area during parsing.  */
26 #define DYNARRAY_STRUCT action_list
27 #define DYNARRAY_ELEMENT struct nss_action
28 #define DYNARRAY_PREFIX action_list_
29 #include <malloc/dynarray-skeleton.c>
30 
31 /* Skip whitespace in line[].  */
32 #define SKIP_WS() \
33   while (line[0] != '\0' && isspace (line[0]))	\
34     ++line;
35 
36 /* Read the source names:
37         `( <source> ( "[" "!"? (<status> "=" <action> )+ "]" )? )*'
38    */
39 static bool
nss_action_parse(const char * line,struct action_list * result)40 nss_action_parse (const char *line, struct action_list *result)
41 {
42   while (1)
43     {
44       SKIP_WS ();
45       if (line[0] == '\0')
46         /* No more sources specified.  */
47         return true;
48 
49       /* Read <source> identifier.  */
50       const char *name = line;
51       while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[')
52         ++line;
53       if (name == line)
54         return true;
55 
56       struct nss_action new_service
57         = { .module = __nss_module_allocate (name, line - name), };
58       if (new_service.module == NULL)
59         {
60           /* Memory allocation error.  */
61           action_list_mark_failed (result);
62           return false;
63         }
64       nss_action_set_all (&new_service, NSS_ACTION_CONTINUE);
65       nss_action_set (&new_service, NSS_STATUS_SUCCESS, NSS_ACTION_RETURN);
66       nss_action_set (&new_service, NSS_STATUS_RETURN, NSS_ACTION_RETURN);
67 
68       SKIP_WS ();
69 
70       if (line[0] == '[')
71         {
72           /* Read criterions.  */
73 
74 	  /* Skip the '['.  */
75 	  ++line;
76 	  SKIP_WS ();
77 
78           do
79             {
80               int not;
81               enum nss_status status;
82               lookup_actions action;
83 
84               /* Grok ! before name to mean all statuses but that one.  */
85               not = line[0] == '!';
86               if (not)
87                 ++line;
88 
89               /* Read status name.  */
90               name = line;
91               while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
92                      && line[0] != ']')
93                 ++line;
94 
95               /* Compare with known statuses.  */
96               if (line - name == 7)
97                 {
98                   if (__strncasecmp (name, "SUCCESS", 7) == 0)
99                     status = NSS_STATUS_SUCCESS;
100                   else if (__strncasecmp (name, "UNAVAIL", 7) == 0)
101                     status = NSS_STATUS_UNAVAIL;
102                   else
103                     return false;
104                 }
105               else if (line - name == 8)
106                 {
107                   if (__strncasecmp (name, "NOTFOUND", 8) == 0)
108                     status = NSS_STATUS_NOTFOUND;
109                   else if (__strncasecmp (name, "TRYAGAIN", 8) == 0)
110                     status = NSS_STATUS_TRYAGAIN;
111                   else
112                     return false;
113                 }
114               else
115 		return false;
116 
117 	      SKIP_WS ();
118               if (line[0] != '=')
119                 return false;
120 
121 	      /* Skip the '='.  */
122 	      ++line;
123 	      SKIP_WS ();
124               name = line;
125               while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
126                      && line[0] != ']')
127                 ++line;
128 
129               if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0)
130                 action = NSS_ACTION_RETURN;
131               else if (line - name == 8
132                        && __strncasecmp (name, "CONTINUE", 8) == 0)
133                 action = NSS_ACTION_CONTINUE;
134               else if (line - name == 5
135                        && __strncasecmp (name, "MERGE", 5) == 0)
136                 action = NSS_ACTION_MERGE;
137               else
138                 return false;
139 
140               if (not)
141                 {
142                   /* Save the current action setting for this status,
143                      set them all to the given action, and reset this one.  */
144                   const lookup_actions save
145                     = nss_action_get (&new_service, status);
146                   nss_action_set_all (&new_service, action);
147                   nss_action_set (&new_service, status, save);
148                 }
149               else
150                 nss_action_set (&new_service, status, action);
151 
152 	      SKIP_WS ();
153             }
154           while (line[0] != ']');
155 
156           /* Skip the ']'.  */
157           ++line;
158         }
159 
160       action_list_add (result, new_service);
161     }
162 }
163 
164 nss_action_list
__nss_action_parse(const char * line)165  __nss_action_parse (const char *line)
166 {
167   struct action_list list;
168   action_list_init (&list);
169   if (nss_action_parse (line, &list))
170     {
171       size_t size;
172       struct nss_action null_service
173         = { .module = NULL, };
174 
175       action_list_add (&list, null_service);
176       size = action_list_size (&list);
177       return __nss_action_allocate (action_list_begin (&list), size);
178     }
179   else if (action_list_has_failed (&list))
180     {
181       /* Memory allocation error.  */
182       __set_errno (ENOMEM);
183       return NULL;
184     }
185   else
186     {
187       /* Parse error.  */
188       __set_errno (EINVAL);
189       return NULL;
190     }
191 }
192