1 | /* Copyright (C) 1997-2016 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 | <http://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <atomic.h> |
20 | #include <nss.h> |
21 | #include <errno.h> |
22 | #include <ctype.h> |
23 | #include <string.h> |
24 | #include <aliases.h> |
25 | #include <libc-lock.h> |
26 | #include <rpcsvc/nis.h> |
27 | |
28 | #include "nss-nisplus.h" |
29 | |
30 | __libc_lock_define_initialized (static, lock) |
31 | |
32 | static nis_result *result; |
33 | static u_long next_entry; |
34 | static nis_name tablename_val; |
35 | static size_t tablename_len; |
36 | |
37 | #define NISENTRYVAL(idx, col, res) \ |
38 | (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_val) |
39 | |
40 | #define NISENTRYLEN(idx, col, res) \ |
41 | (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_len) |
42 | |
43 | static enum nss_status |
44 | _nss_create_tablename (int *errnop) |
45 | { |
46 | if (tablename_val == NULL) |
47 | { |
48 | const char *local_dir = nis_local_directory (); |
49 | size_t local_dir_len = strlen (local_dir); |
50 | static const char prefix[] = "mail_aliases.org_dir." ; |
51 | |
52 | char *p = malloc (sizeof (prefix) + local_dir_len); |
53 | if (p == NULL) |
54 | { |
55 | *errnop = errno; |
56 | return NSS_STATUS_TRYAGAIN; |
57 | } |
58 | |
59 | memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1); |
60 | |
61 | tablename_len = sizeof (prefix) - 1 + local_dir_len; |
62 | |
63 | atomic_write_barrier (); |
64 | |
65 | tablename_val = p; |
66 | } |
67 | |
68 | return NSS_STATUS_SUCCESS; |
69 | } |
70 | |
71 | static int |
72 | _nss_nisplus_parse_aliasent (nis_result *result, unsigned long entry, |
73 | struct aliasent *alias, char *buffer, |
74 | size_t buflen, int *errnop) |
75 | { |
76 | if (result == NULL) |
77 | return 0; |
78 | |
79 | if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS) |
80 | || __type_of (&NIS_RES_OBJECT (result)[entry]) != NIS_ENTRY_OBJ |
81 | || strcmp (NIS_RES_OBJECT (result)[entry].EN_data.en_type, |
82 | "mail_aliases" ) != 0 |
83 | || NIS_RES_OBJECT (result)[entry].EN_data.en_cols.en_cols_len < 2) |
84 | return 0; |
85 | |
86 | if (NISENTRYLEN (entry, 1, result) >= buflen) |
87 | { |
88 | /* The line is too long for our buffer. */ |
89 | no_more_room: |
90 | *errnop = ERANGE; |
91 | return -1; |
92 | } |
93 | |
94 | char *cp = __stpncpy (buffer, NISENTRYVAL (entry, 1, result), |
95 | NISENTRYLEN (entry, 1, result)); |
96 | *cp = '\0'; |
97 | |
98 | char *first_unused = cp + 1; |
99 | size_t room_left = buflen - (first_unused - buffer); |
100 | |
101 | alias->alias_local = 0; |
102 | alias->alias_members_len = 0; |
103 | |
104 | if (NISENTRYLEN (entry, 0, result) >= room_left) |
105 | goto no_more_room; |
106 | |
107 | cp = __stpncpy (first_unused, NISENTRYVAL (entry, 0, result), |
108 | NISENTRYLEN (entry, 0, result)); |
109 | *cp = '\0'; |
110 | alias->alias_name = first_unused; |
111 | |
112 | /* Terminate the line for any case. */ |
113 | cp = strpbrk (alias->alias_name, "#\n" ); |
114 | if (cp != NULL) |
115 | *cp = '\0'; |
116 | |
117 | size_t len = strlen (alias->alias_name) + 1; |
118 | first_unused += len; |
119 | room_left -= len; |
120 | |
121 | /* Adjust the pointer so it is aligned for |
122 | storing pointers. */ |
123 | size_t adjust = ((__alignof__ (char *) |
124 | - (first_unused - (char *) 0) % __alignof__ (char *)) |
125 | % __alignof__ (char *)); |
126 | if (room_left < adjust) |
127 | goto no_more_room; |
128 | first_unused += adjust; |
129 | room_left -= adjust; |
130 | |
131 | alias->alias_members = (char **) first_unused; |
132 | |
133 | char *line = buffer; |
134 | while (*line != '\0') |
135 | { |
136 | /* Skip leading blanks. */ |
137 | while (isspace (*line)) |
138 | ++line; |
139 | |
140 | if (*line == '\0') |
141 | break; |
142 | |
143 | if (room_left < sizeof (char *)) |
144 | goto no_more_room; |
145 | room_left -= sizeof (char *); |
146 | alias->alias_members[alias->alias_members_len] = line; |
147 | |
148 | while (*line != '\0' && *line != ',') |
149 | ++line; |
150 | |
151 | if (line != alias->alias_members[alias->alias_members_len]) |
152 | { |
153 | *line++ = '\0'; |
154 | ++alias->alias_members_len; |
155 | } |
156 | else if (*line == ',') |
157 | ++line; |
158 | } |
159 | |
160 | return alias->alias_members_len == 0 ? 0 : 1; |
161 | } |
162 | |
163 | static enum nss_status |
164 | internal_setaliasent (void) |
165 | { |
166 | enum nss_status status; |
167 | int err; |
168 | |
169 | if (result != NULL) |
170 | { |
171 | nis_freeresult (result); |
172 | result = NULL; |
173 | } |
174 | |
175 | if (_nss_create_tablename (&err) != NSS_STATUS_SUCCESS) |
176 | return NSS_STATUS_UNAVAIL; |
177 | |
178 | next_entry = 0; |
179 | result = nis_list (tablename_val, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL); |
180 | if (result == NULL) |
181 | { |
182 | status = NSS_STATUS_TRYAGAIN; |
183 | __set_errno (ENOMEM); |
184 | } |
185 | else |
186 | { |
187 | status = niserr2nss (result->status); |
188 | if (status != NSS_STATUS_SUCCESS) |
189 | { |
190 | nis_freeresult (result); |
191 | result = NULL; |
192 | } |
193 | } |
194 | return status; |
195 | } |
196 | |
197 | enum nss_status |
198 | _nss_nisplus_setaliasent (void) |
199 | { |
200 | enum nss_status status; |
201 | |
202 | __libc_lock_lock (lock); |
203 | |
204 | status = internal_setaliasent (); |
205 | |
206 | __libc_lock_unlock (lock); |
207 | |
208 | return status; |
209 | } |
210 | |
211 | enum nss_status |
212 | _nss_nisplus_endaliasent (void) |
213 | { |
214 | __libc_lock_lock (lock); |
215 | |
216 | if (result != NULL) |
217 | { |
218 | nis_freeresult (result); |
219 | result = NULL; |
220 | } |
221 | next_entry = 0; |
222 | |
223 | __libc_lock_unlock (lock); |
224 | |
225 | return NSS_STATUS_SUCCESS; |
226 | } |
227 | |
228 | static enum nss_status |
229 | internal_nisplus_getaliasent_r (struct aliasent *alias, |
230 | char *buffer, size_t buflen, int *errnop) |
231 | { |
232 | int parse_res; |
233 | |
234 | if (result == NULL) |
235 | { |
236 | enum nss_status status; |
237 | |
238 | status = internal_setaliasent (); |
239 | if (result == NULL || status != NSS_STATUS_SUCCESS) |
240 | return status; |
241 | } |
242 | |
243 | /* Get the next entry until we found a correct one. */ |
244 | do |
245 | { |
246 | if (next_entry >= result->objects.objects_len) |
247 | return NSS_STATUS_NOTFOUND; |
248 | |
249 | parse_res = _nss_nisplus_parse_aliasent (result, next_entry, alias, |
250 | buffer, buflen, errnop); |
251 | if (parse_res == -1) |
252 | return NSS_STATUS_TRYAGAIN; |
253 | |
254 | ++next_entry; |
255 | } |
256 | while (!parse_res); |
257 | |
258 | return NSS_STATUS_SUCCESS; |
259 | } |
260 | |
261 | enum nss_status |
262 | _nss_nisplus_getaliasent_r (struct aliasent *result, char *buffer, |
263 | size_t buflen, int *errnop) |
264 | { |
265 | int status; |
266 | |
267 | __libc_lock_lock (lock); |
268 | |
269 | status = internal_nisplus_getaliasent_r (result, buffer, buflen, errnop); |
270 | |
271 | __libc_lock_unlock (lock); |
272 | |
273 | return status; |
274 | } |
275 | |
276 | enum nss_status |
277 | _nss_nisplus_getaliasbyname_r (const char *name, struct aliasent *alias, |
278 | char *buffer, size_t buflen, int *errnop) |
279 | { |
280 | int parse_res; |
281 | |
282 | if (tablename_val == NULL) |
283 | { |
284 | __libc_lock_lock (lock); |
285 | |
286 | enum nss_status status = _nss_create_tablename (errnop); |
287 | |
288 | __libc_lock_unlock (lock); |
289 | |
290 | if (status != NSS_STATUS_SUCCESS) |
291 | return status; |
292 | } |
293 | |
294 | if (name == NULL) |
295 | { |
296 | *errnop = EINVAL; |
297 | return NSS_STATUS_UNAVAIL; |
298 | } |
299 | |
300 | char buf[strlen (name) + 9 + tablename_len]; |
301 | int olderr = errno; |
302 | |
303 | snprintf (buf, sizeof (buf), "[name=%s],%s" , name, tablename_val); |
304 | |
305 | nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS, NULL, NULL); |
306 | |
307 | if (result == NULL) |
308 | { |
309 | *errnop = ENOMEM; |
310 | return NSS_STATUS_TRYAGAIN; |
311 | } |
312 | |
313 | if (__glibc_unlikely (niserr2nss (result->status) != NSS_STATUS_SUCCESS)) |
314 | { |
315 | enum nss_status status = niserr2nss (result->status); |
316 | nis_freeresult (result); |
317 | return status; |
318 | } |
319 | |
320 | parse_res = _nss_nisplus_parse_aliasent (result, 0, alias, |
321 | buffer, buflen, errnop); |
322 | |
323 | /* We do not need the lookup result anymore. */ |
324 | nis_freeresult (result); |
325 | |
326 | if (__glibc_unlikely (parse_res < 1)) |
327 | { |
328 | __set_errno (olderr); |
329 | |
330 | if (parse_res == -1) |
331 | return NSS_STATUS_TRYAGAIN; |
332 | else |
333 | return NSS_STATUS_NOTFOUND; |
334 | } |
335 | |
336 | return NSS_STATUS_SUCCESS; |
337 | } |
338 | |