Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Stephen Gallagher <sgallagh@redhat.com>
6 :
7 : Copyright (C) 2012 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include <nss.h>
24 : #include <netdb.h>
25 : #include <errno.h>
26 : #include <sys/types.h>
27 : #include <unistd.h>
28 : #include <stdlib.h>
29 : #include <stdint.h>
30 : #include <stdio.h>
31 : #include <string.h>
32 : #include "sss_cli.h"
33 :
34 : static struct sss_nss_getservent_data {
35 : size_t len;
36 : size_t ptr;
37 : uint8_t *data;
38 : } sss_nss_getservent_data;
39 :
40 0 : static void sss_nss_getservent_data_clean(void) {
41 :
42 0 : if (sss_nss_getservent_data.data != NULL) {
43 0 : free(sss_nss_getservent_data.data);
44 0 : sss_nss_getservent_data.data = NULL;
45 : }
46 0 : sss_nss_getservent_data.len = 0;
47 0 : sss_nss_getservent_data.ptr = 0;
48 0 : }
49 :
50 : /* GETSERVBYNAME Request
51 : *
52 : * 0-X: Sequence of two, zero-terminated strings (name, protocol).
53 : * Protocol may be zero-length to imply "any"
54 : *
55 : * GETSERVBYPORT Request:
56 : * 0-3: 16-bit port number in network byte order
57 : * 4-15: Reserved/padding
58 : * 16-X: Zero-terminated string (protocol)
59 : * Protocol may be zero-length to imply "any"
60 : *
61 : * Replies:
62 : * 0-3: 32-bit unsigned number of results
63 : * 4-7: 32-bit unsigned (reserved/padding)
64 : * 7-X: Result data (blocks equal to number of results)
65 : *
66 : * Result data:
67 : * 0-3: 32-bit unsigned port number in network byte order
68 : * 4-7: 32-bit unsigned number of aliases
69 : * 8-X: sequence of zero-terminated strings
70 : * (name, protocol, zero or more aliases)
71 : */
72 : struct sss_nss_svc_rep {
73 : struct servent *result;
74 : char *buffer;
75 : size_t buflen;
76 : };
77 :
78 : #define SVC_METADATA_COUNT 8
79 :
80 : static errno_t
81 0 : sss_nss_getsvc_readrep(struct sss_nss_svc_rep *sr,
82 : uint8_t *buf, size_t *len)
83 : {
84 : errno_t ret;
85 : uint32_t c;
86 : uint32_t num_aliases;
87 : size_t i, l, slen, dlen, pad, ptaliases, alen;
88 : char *sbuf;
89 :
90 : /* Buffer must contain two 32-bit integers,
91 : * at least one character and null-terminator
92 : * for the name, and at least a null-
93 : * terminator for the protocol.
94 : */
95 0 : if (*len < 11) {
96 : /* not enough space for data, bad packet */
97 0 : return EBADMSG;
98 : }
99 :
100 : /* Get the port */
101 0 : SAFEALIGN_COPY_UINT32(&c, buf, NULL);
102 0 : sr->result->s_port = (uint16_t)c;
103 :
104 : /* Get the number of aliases */
105 0 : SAFEALIGN_COPY_UINT32(&num_aliases, buf + sizeof(uint32_t), NULL);
106 :
107 0 : sbuf = (char *)&buf[2 * sizeof(uint32_t)];
108 0 : slen = *len - (2 * sizeof(uint32_t));
109 0 : dlen = sr->buflen;
110 :
111 : /* Copy in the name */
112 0 : i = 0;
113 0 : sr->result->s_name = &(sr->buffer[i]);
114 :
115 0 : ret = sss_readrep_copy_string(sbuf, &i,
116 : &slen, &dlen,
117 0 : &sr->result->s_name,
118 : NULL);
119 0 : if (ret != EOK) return ret;
120 :
121 : /* Copy in the protocol */
122 0 : sr->result->s_proto = &(sr->buffer[i]);
123 :
124 0 : ret = sss_readrep_copy_string(sbuf, &i,
125 : &slen, &dlen,
126 0 : &sr->result->s_proto,
127 : NULL);
128 0 : if (ret != EOK) return ret;
129 :
130 : /* Make sure sr->buffer[i+pad] is aligned to sizeof(char *) */
131 0 : pad = PADDING_SIZE(i, char *);
132 :
133 : /* Copy in the aliases */
134 0 : sr->result->s_aliases = DISCARD_ALIGN(&(sr->buffer[i+pad]), char **);
135 :
136 0 : ptaliases = (sizeof(char *) * (num_aliases + 1)) + pad;
137 0 : if (ptaliases > dlen) {
138 0 : return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
139 : }
140 :
141 0 : dlen -= ptaliases;
142 0 : ptaliases += i;
143 0 : sr->result->s_aliases[num_aliases] = NULL; /* terminate array */
144 :
145 0 : for (l = 0; l < num_aliases; l++) {
146 0 : sr->result->s_aliases[l] = &(sr->buffer[ptaliases]);
147 0 : ret = sss_readrep_copy_string(sbuf, &i,
148 : &slen, &dlen,
149 0 : &sr->result->s_aliases[l],
150 : &alen);
151 0 : if (ret != EOK) return ret;
152 :
153 0 : ptaliases += alen + 1;
154 : }
155 :
156 0 : *len = slen - i;
157 :
158 0 : return EOK;
159 : }
160 :
161 : enum nss_status
162 0 : _nss_sss_getservbyname_r(const char *name,
163 : const char *protocol,
164 : struct servent *result,
165 : char *buffer, size_t buflen,
166 : int *errnop)
167 : {
168 : struct sss_cli_req_data rd;
169 : struct sss_nss_svc_rep svcrep;
170 : size_t name_len;
171 0 : size_t proto_len = 0;
172 : uint8_t *repbuf;
173 : uint8_t *data;
174 : size_t replen, len;
175 : uint32_t num_results;
176 : enum nss_status nret;
177 : int ret;
178 :
179 : /* Caught once glibc passing in buffer == 0x0 */
180 0 : if (!buffer || !buflen) return ERANGE;
181 :
182 0 : ret = sss_strnlen(name, SSS_NAME_MAX, &name_len);
183 0 : if (ret != 0) {
184 0 : *errnop = EINVAL;
185 0 : return NSS_STATUS_NOTFOUND;
186 : }
187 :
188 0 : if (protocol) {
189 0 : ret = sss_strnlen(protocol, SSS_NAME_MAX, &proto_len);
190 0 : if (ret != 0) {
191 0 : *errnop = EINVAL;
192 0 : return NSS_STATUS_NOTFOUND;
193 : }
194 : }
195 :
196 0 : rd.len = name_len + proto_len + 2;
197 0 : data = malloc(sizeof(uint8_t)*rd.len);
198 0 : if (data == NULL) {
199 0 : nret = NSS_STATUS_TRYAGAIN;
200 0 : goto out;
201 : }
202 :
203 0 : memcpy(data, name, name_len + 1);
204 :
205 0 : if (protocol) {
206 0 : memcpy(data + name_len + 1, protocol, proto_len + 1);
207 : } else {
208 : /* No protocol specified, pass empty string */
209 0 : data[name_len + 1] = '\0';
210 : }
211 0 : rd.data = data;
212 :
213 0 : sss_nss_lock();
214 :
215 0 : nret = sss_nss_make_request(SSS_NSS_GETSERVBYNAME, &rd,
216 : &repbuf, &replen, errnop);
217 0 : free(data);
218 0 : if (nret != NSS_STATUS_SUCCESS) {
219 0 : goto out;
220 : }
221 :
222 0 : svcrep.result = result;
223 0 : svcrep.buffer = buffer;
224 0 : svcrep.buflen = buflen;
225 :
226 : /* Get number of results from repbuf. */
227 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
228 :
229 : /* no results if not found */
230 0 : if (num_results == 0) {
231 0 : free(repbuf);
232 0 : nret = NSS_STATUS_NOTFOUND;
233 0 : goto out;
234 : }
235 :
236 : /* only 1 result is accepted for this function */
237 0 : if (num_results != 1) {
238 0 : *errnop = EBADMSG;
239 0 : free(repbuf);
240 0 : nret = NSS_STATUS_TRYAGAIN;
241 0 : goto out;
242 : }
243 :
244 0 : len = replen - SVC_METADATA_COUNT;
245 0 : ret = sss_nss_getsvc_readrep(&svcrep,
246 : repbuf + SVC_METADATA_COUNT,
247 : &len);
248 0 : free(repbuf);
249 0 : if (ret) {
250 0 : *errnop = ret;
251 0 : nret = NSS_STATUS_TRYAGAIN;
252 0 : goto out;
253 : }
254 :
255 0 : nret = NSS_STATUS_SUCCESS;
256 :
257 : out:
258 0 : sss_nss_unlock();
259 0 : return nret;
260 : }
261 :
262 :
263 : enum nss_status
264 0 : _nss_sss_getservbyport_r(int port, const char *protocol,
265 : struct servent *result,
266 : char *buffer, size_t buflen,
267 : int *errnop)
268 : {
269 : struct sss_cli_req_data rd;
270 : struct sss_nss_svc_rep svcrep;
271 0 : size_t proto_len = 0;
272 : uint8_t *repbuf;
273 : uint8_t *data;
274 0 : size_t p = 0;
275 : size_t replen, len;
276 : uint32_t num_results;
277 : enum nss_status nret;
278 : int ret;
279 :
280 : /* Caught once glibc passing in buffer == 0x0 */
281 0 : if (!buffer || !buflen) return ERANGE;
282 :
283 0 : if (protocol) {
284 0 : ret = sss_strnlen(protocol, SSS_NAME_MAX, &proto_len);
285 0 : if (ret != 0) {
286 0 : *errnop = EINVAL;
287 0 : return NSS_STATUS_NOTFOUND;
288 : }
289 : }
290 :
291 0 : rd.len = sizeof(uint32_t)*2 + proto_len + 1;
292 0 : data = malloc(sizeof(uint8_t)*rd.len);
293 0 : if (data == NULL) {
294 0 : nret = NSS_STATUS_TRYAGAIN;
295 0 : goto out;
296 : }
297 :
298 0 : SAFEALIGN_SET_UINT16(data, port, &p);
299 :
300 : /* Padding */
301 0 : SAFEALIGN_SET_UINT16(data + p, 0, &p);
302 0 : SAFEALIGN_SET_UINT32(data + p, 0, &p);
303 :
304 0 : if (protocol) {
305 0 : memcpy(data + p, protocol, proto_len + 1);
306 : } else {
307 : /* No protocol specified, pass empty string */
308 0 : data[p] = '\0';
309 : }
310 0 : rd.data = data;
311 :
312 0 : sss_nss_lock();
313 :
314 0 : nret = sss_nss_make_request(SSS_NSS_GETSERVBYPORT, &rd,
315 : &repbuf, &replen, errnop);
316 0 : free(data);
317 0 : if (nret != NSS_STATUS_SUCCESS) {
318 0 : goto out;
319 : }
320 :
321 0 : svcrep.result = result;
322 0 : svcrep.buffer = buffer;
323 0 : svcrep.buflen = buflen;
324 :
325 : /* Get number of results from repbuf. */
326 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
327 :
328 : /* no results if not found */
329 0 : if (num_results == 0) {
330 0 : free(repbuf);
331 0 : nret = NSS_STATUS_NOTFOUND;
332 0 : goto out;
333 : }
334 :
335 : /* only 1 result is accepted for this function */
336 0 : if (num_results != 1) {
337 0 : *errnop = EBADMSG;
338 0 : free(repbuf);
339 0 : nret = NSS_STATUS_TRYAGAIN;
340 0 : goto out;
341 : }
342 :
343 0 : len = replen - SVC_METADATA_COUNT;
344 0 : ret = sss_nss_getsvc_readrep(&svcrep,
345 : repbuf + SVC_METADATA_COUNT,
346 : &len);
347 0 : free(repbuf);
348 0 : if (ret) {
349 0 : *errnop = ret;
350 0 : nret = NSS_STATUS_TRYAGAIN;
351 0 : goto out;
352 : }
353 :
354 0 : nret = NSS_STATUS_SUCCESS;
355 :
356 : out:
357 0 : sss_nss_unlock();
358 0 : return nret;
359 : }
360 :
361 :
362 : enum nss_status
363 0 : _nss_sss_setservent(void)
364 : {
365 : enum nss_status nret;
366 : int errnop;
367 0 : sss_nss_lock();
368 :
369 : /* make sure we do not have leftovers, and release memory */
370 0 : sss_nss_getservent_data_clean();
371 :
372 0 : nret = sss_nss_make_request(SSS_NSS_SETSERVENT,
373 : NULL, NULL, NULL, &errnop);
374 0 : if (nret != NSS_STATUS_SUCCESS) {
375 0 : errno = errnop;
376 : }
377 :
378 0 : sss_nss_unlock();
379 0 : return nret;
380 : }
381 :
382 : static enum nss_status internal_getservent_r(struct servent *result,
383 : char *buffer, size_t buflen,
384 : int *errnop);
385 :
386 : enum nss_status
387 0 : _nss_sss_getservent_r(struct servent *result,
388 : char *buffer, size_t buflen,
389 : int *errnop)
390 : {
391 : enum nss_status nret;
392 :
393 0 : sss_nss_lock();
394 0 : nret = internal_getservent_r(result, buffer, buflen, errnop);
395 0 : sss_nss_unlock();
396 :
397 0 : return nret;
398 : }
399 :
400 0 : static enum nss_status internal_getservent_r(struct servent *result,
401 : char *buffer, size_t buflen,
402 : int *errnop)
403 : {
404 : struct sss_cli_req_data rd;
405 : struct sss_nss_svc_rep pwrep;
406 : uint8_t *repbuf;
407 : size_t replen;
408 : uint32_t num_results;
409 : enum nss_status nret;
410 : uint32_t num_entries;
411 : int ret;
412 :
413 : /* Caught once glibc passing in buffer == 0x0 */
414 0 : if (!buffer || !buflen) return ERANGE;
415 :
416 : /* if there are leftovers return the next one */
417 0 : if (sss_nss_getservent_data.data != NULL &&
418 0 : sss_nss_getservent_data.ptr < sss_nss_getservent_data.len) {
419 :
420 0 : repbuf = sss_nss_getservent_data.data + sss_nss_getservent_data.ptr;
421 0 : replen = sss_nss_getservent_data.len - sss_nss_getservent_data.ptr;
422 :
423 0 : pwrep.result = result;
424 0 : pwrep.buffer = buffer;
425 0 : pwrep.buflen = buflen;
426 :
427 0 : ret = sss_nss_getsvc_readrep(&pwrep, repbuf, &replen);
428 0 : if (ret) {
429 0 : *errnop = ret;
430 0 : return NSS_STATUS_TRYAGAIN;
431 : }
432 :
433 : /* advance buffer pointer */
434 0 : sss_nss_getservent_data.ptr = sss_nss_getservent_data.len - replen;
435 :
436 0 : return NSS_STATUS_SUCCESS;
437 : }
438 :
439 : /* release memory if any */
440 0 : sss_nss_getservent_data_clean();
441 :
442 : /* retrieve no more than SSS_NSS_MAX_ENTRIES at a time */
443 0 : num_entries = SSS_NSS_MAX_ENTRIES;
444 0 : rd.len = sizeof(uint32_t);
445 0 : rd.data = &num_entries;
446 :
447 0 : nret = sss_nss_make_request(SSS_NSS_GETSERVENT, &rd,
448 : &repbuf, &replen, errnop);
449 0 : if (nret != NSS_STATUS_SUCCESS) {
450 0 : return nret;
451 : }
452 :
453 : /* Get number of results from repbuf */
454 0 : SAFEALIGN_COPY_UINT32(&num_results, repbuf, NULL);
455 :
456 : /* no results if not found */
457 0 : if ((num_results == 0) || (replen - SVC_METADATA_COUNT == 0)) {
458 0 : free(repbuf);
459 0 : return NSS_STATUS_NOTFOUND;
460 : }
461 :
462 0 : sss_nss_getservent_data.data = repbuf;
463 0 : sss_nss_getservent_data.len = replen;
464 :
465 : /* skip metadata fields */
466 0 : sss_nss_getservent_data.ptr = SVC_METADATA_COUNT;
467 :
468 : /* call again ourselves, this will return the first result */
469 0 : return internal_getservent_r(result, buffer, buflen, errnop);
470 : }
471 :
472 :
473 : enum nss_status
474 0 : _nss_sss_endservent(void)
475 : {
476 : enum nss_status nret;
477 : int errnop;
478 :
479 0 : sss_nss_lock();
480 :
481 : /* make sure we do not have leftovers, and release memory */
482 0 : sss_nss_getservent_data_clean();
483 :
484 0 : nret = sss_nss_make_request(SSS_NSS_ENDSERVENT,
485 : NULL, NULL, NULL, &errnop);
486 0 : if (nret != NSS_STATUS_SUCCESS) {
487 0 : errno = errnop;
488 : }
489 :
490 0 : sss_nss_unlock();
491 0 : return nret;
492 : }
|