1/*
2 * Copyright (C) 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29/*
30 * Copyright (c) 1983, 1993, 1994
31 * The Regents of the University of California. All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58#include <sys/param.h>
59#include <sys/poll.h>
60#include <sys/socket.h>
61#include <sys/stat.h>
62
63#include <netinet/in.h>
64#include <arpa/inet.h>
65
66#include <alloca.h>
67#include <signal.h>
68#include <fcntl.h>
69#include <netdb.h>
70#include <unistd.h>
71#include <pwd.h>
72#include <errno.h>
73#include <stdio.h>
74#include <stdio_ext.h>
75#include <ctype.h>
76#include <string.h>
77#include <libintl.h>
78#include <stdlib.h>
79#include <wchar.h>
80#include <sys/uio.h>
81
82
83int __ivaliduser (FILE *, u_int32_t, const char *, const char *);
84static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
85 const char *, const char *, const char *);
86static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
87 int superuser, const char *ruser,
88 const char *luser, const char *rhost);
89static int ruserok_sa (struct sockaddr *ra, size_t ralen,
90 int superuser, const char *ruser,
91 const char *luser);
92int iruserok_af (const void *raddr, int superuser, const char *ruser,
93 const char *luser, sa_family_t af);
94int iruserok (u_int32_t raddr, int superuser, const char *ruser,
95 const char *luser);
96
97libc_hidden_proto (iruserok_af)
98
99libc_freeres_ptr(static char *ahostbuf);
100
101int
102rcmd_af (char **ahost, u_short rport, const char *locuser, const char *remuser,
103 const char *cmd, int *fd2p, sa_family_t af)
104{
105 char paddr[INET6_ADDRSTRLEN];
106 struct addrinfo hints, *res, *ai;
107 union
108 {
109 struct sockaddr sa;
110 struct sockaddr_storage ss;
111 struct sockaddr_in sin;
112 struct sockaddr_in6 sin6;
113 } from;
114 struct pollfd pfd[2];
115 int32_t oldmask;
116 pid_t pid;
117 int s, lport, timo, error;
118 char c;
119 int refused;
120 char num[8];
121 ssize_t n;
122
123 if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
124 {
125 __set_errno (EAFNOSUPPORT);
126 return -1;
127 }
128
129 pid = __getpid();
130
131 memset(&hints, '\0', sizeof(hints));
132 hints.ai_flags = AI_CANONNAME;
133 hints.ai_family = af;
134 hints.ai_socktype = SOCK_STREAM;
135 (void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
136 error = getaddrinfo(*ahost, num, &hints, &res);
137 if (error) {
138 if (error == EAI_NONAME && *ahost != NULL)
139 __fxprintf(NULL, "%s: Unknown host\n", *ahost);
140 else
141 __fxprintf(NULL, "rcmd: getaddrinfo: %s\n",
142 gai_strerror(error));
143
144 return -1;
145 }
146
147 pfd[0].events = POLLIN;
148 pfd[1].events = POLLIN;
149
150 if (res->ai_canonname){
151 free (ahostbuf);
152 ahostbuf = strdup (res->ai_canonname);
153 if (ahostbuf == NULL) {
154 __fxprintf(NULL, "%s",
155 _("rcmd: Cannot allocate memory\n"));
156 return -1;
157 }
158 *ahost = ahostbuf;
159 } else
160 *ahost = NULL;
161 ai = res;
162 refused = 0;
163 oldmask = __sigblock(sigmask(SIGURG));
164 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
165 char errbuf[200];
166
167 s = rresvport_af(&lport, ai->ai_family);
168 if (s < 0) {
169 if (errno == EAGAIN)
170 __fxprintf(NULL, "%s", _("\
171rcmd: socket: All ports in use\n"));
172 else
173 __fxprintf(NULL, "rcmd: socket: %m\n");
174
175 __sigsetmask(oldmask);
176 freeaddrinfo(res);
177 return -1;
178 }
179 __fcntl(s, F_SETOWN, pid);
180 if (__connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
181 break;
182 (void)__close(s);
183 if (errno == EADDRINUSE) {
184 lport--;
185 continue;
186 }
187 if (errno == ECONNREFUSED)
188 refused = 1;
189 if (ai->ai_next != NULL) {
190 int oerrno = errno;
191 char *buf = NULL;
192
193 getnameinfo(ai->ai_addr, ai->ai_addrlen,
194 paddr, sizeof(paddr),
195 NULL, 0,
196 NI_NUMERICHOST);
197
198 if (__asprintf (&buf, _("connect to address %s: "),
199 paddr) >= 0)
200 {
201 __fxprintf(NULL, "%s", buf);
202 free (buf);
203 }
204 __set_errno (oerrno);
205 perror(0);
206 ai = ai->ai_next;
207 getnameinfo(ai->ai_addr, ai->ai_addrlen,
208 paddr, sizeof(paddr),
209 NULL, 0,
210 NI_NUMERICHOST);
211 if (__asprintf (&buf, _("Trying %s...\n"), paddr) >= 0)
212 {
213 __fxprintf (NULL, "%s", buf);
214 free (buf);
215 }
216 continue;
217 }
218 if (refused && timo <= 16) {
219 (void)__sleep(timo);
220 timo *= 2;
221 ai = res;
222 refused = 0;
223 continue;
224 }
225 freeaddrinfo(res);
226 (void)__fxprintf(NULL, "%s: %s\n", *ahost,
227 __strerror_r(errno, errbuf, sizeof (errbuf)));
228 __sigsetmask(oldmask);
229 return -1;
230 }
231 lport--;
232 if (fd2p == 0) {
233 __write(s, "", 1);
234 lport = 0;
235 } else {
236 char num[8];
237 int s2 = rresvport_af(&lport, ai->ai_family), s3;
238 socklen_t len = ai->ai_addrlen;
239
240 if (s2 < 0)
241 goto bad;
242 __listen(s2, 1);
243 (void)__snprintf(num, sizeof(num), "%d", lport);
244 if (__write(s, num, strlen(num)+1) != (ssize_t)strlen(num)+1) {
245 char *buf = NULL;
246
247 if (__asprintf (&buf, _("\
248rcmd: write (setting up stderr): %m\n")) >= 0)
249 {
250 __fxprintf(NULL, "%s", buf);
251 free (buf);
252 }
253 (void)__close(s2);
254 goto bad;
255 }
256 pfd[0].fd = s;
257 pfd[1].fd = s2;
258 __set_errno (0);
259 if (__poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
260 char *buf = NULL;
261
262 if ((errno != 0
263 && __asprintf(&buf, _("\
264rcmd: poll (setting up stderr): %m\n")) >= 0)
265 || (errno == 0
266 && __asprintf(&buf, _("\
267poll: protocol failure in circuit setup\n")) >= 0))
268 {
269 __fxprintf (NULL, "%s", buf);
270 free (buf);
271 }
272 (void)__close(s2);
273 goto bad;
274 }
275 s3 = TEMP_FAILURE_RETRY (accept(s2, &from.sa, &len));
276 switch (from.sa.sa_family) {
277 case AF_INET:
278 rport = ntohs(from.sin.sin_port);
279 break;
280 case AF_INET6:
281 rport = ntohs(from.sin6.sin6_port);
282 break;
283 default:
284 rport = 0;
285 break;
286 }
287 (void)__close(s2);
288 if (s3 < 0) {
289 (void)__fxprintf(NULL, "rcmd: accept: %m\n");
290 lport = 0;
291 goto bad;
292 }
293 *fd2p = s3;
294
295 if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
296 char *buf = NULL;
297
298 if (__asprintf(&buf, _("\
299socket: protocol failure in circuit setup\n")) >= 0)
300 {
301 __fxprintf (NULL, "%s", buf);
302 free (buf);
303 }
304 goto bad2;
305 }
306 }
307 struct iovec iov[3] =
308 {
309 [0] = { .iov_base = (void *) locuser,
310 .iov_len = strlen (locuser) + 1 },
311 [1] = { .iov_base = (void *) remuser,
312 .iov_len = strlen (remuser) + 1 },
313 [2] = { .iov_base = (void *) cmd,
314 .iov_len = strlen (cmd) + 1 }
315 };
316 (void) TEMP_FAILURE_RETRY (__writev (s, iov, 3));
317 n = TEMP_FAILURE_RETRY (__read(s, &c, 1));
318 if (n != 1) {
319 char *buf = NULL;
320
321 if ((n == 0
322 && __asprintf(&buf, _("rcmd: %s: short read"),
323 *ahost) >= 0)
324 || (n != 0
325 && __asprintf(&buf, "rcmd: %s: %m\n", *ahost) >= 0))
326 {
327 __fxprintf (NULL, "%s", buf);
328 free (buf);
329 }
330 goto bad2;
331 }
332 if (c != 0) {
333 while (__read(s, &c, 1) == 1) {
334 (void)__write(STDERR_FILENO, &c, 1);
335 if (c == '\n')
336 break;
337 }
338 goto bad2;
339 }
340 __sigsetmask(oldmask);
341 freeaddrinfo(res);
342 return s;
343bad2:
344 if (lport)
345 (void)__close(*fd2p);
346bad:
347 (void)__close(s);
348 __sigsetmask(oldmask);
349 freeaddrinfo(res);
350 return -1;
351}
352libc_hidden_def (rcmd_af)
353
354int
355rcmd (char **ahost, u_short rport, const char *locuser, const char *remuser,
356 const char *cmd, int *fd2p)
357{
358 return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
359}
360
361int
362rresvport_af (int *alport, sa_family_t family)
363{
364 union {
365 struct sockaddr generic;
366 struct sockaddr_in in;
367 struct sockaddr_in6 in6;
368 } ss;
369 int s;
370 size_t len;
371 uint16_t *sport;
372
373 switch(family){
374 case AF_INET:
375 len = sizeof(struct sockaddr_in);
376 sport = &ss.in.sin_port;
377 break;
378 case AF_INET6:
379 len = sizeof(struct sockaddr_in6);
380 sport = &ss.in6.sin6_port;
381 break;
382 default:
383 __set_errno (EAFNOSUPPORT);
384 return -1;
385 }
386 s = __socket(family, SOCK_STREAM, 0);
387 if (s < 0)
388 return -1;
389
390 memset (&ss, '\0', sizeof(ss));
391#ifdef SALEN
392 ss.generic.__ss_len = len;
393#endif
394 ss.generic.sa_family = family;
395
396 /* Ignore invalid values. */
397 if (*alport < IPPORT_RESERVED / 2)
398 *alport = IPPORT_RESERVED / 2;
399 else if (*alport >= IPPORT_RESERVED)
400 *alport = IPPORT_RESERVED - 1;
401
402 int start = *alport;
403 do {
404 *sport = htons((uint16_t) *alport);
405 if (__bind(s, &ss.generic, len) >= 0)
406 return s;
407 if (errno != EADDRINUSE) {
408 (void)__close(s);
409 return -1;
410 }
411 if ((*alport)-- == IPPORT_RESERVED/2)
412 *alport = IPPORT_RESERVED - 1;
413 } while (*alport != start);
414 (void)__close(s);
415 __set_errno (EAGAIN);
416 return -1;
417}
418libc_hidden_def (rresvport_af)
419
420int
421rresvport (int *alport)
422{
423 return rresvport_af(alport, AF_INET);
424}
425
426int __check_rhosts_file = 1;
427char *__rcmd_errstr;
428
429int
430ruserok_af (const char *rhost, int superuser, const char *ruser,
431 const char *luser, sa_family_t af)
432{
433 struct addrinfo hints, *res, *res0;
434 int gai;
435 int ret;
436
437 memset (&hints, '\0', sizeof(hints));
438 hints.ai_family = af;
439 gai = getaddrinfo(rhost, NULL, &hints, &res0);
440 if (gai)
441 return -1;
442 ret = -1;
443 for (res=res0; res; res=res->ai_next)
444 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
445 superuser, ruser, luser, rhost) == 0){
446 ret = 0;
447 break;
448 }
449 freeaddrinfo(res0);
450 return (ret);
451}
452libc_hidden_def (ruserok_af)
453
454int
455ruserok (const char *rhost, int superuser, const char *ruser,
456 const char *luser)
457{
458 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
459}
460
461/* Extremely paranoid file open function. */
462static FILE *
463iruserfopen (const char *file, uid_t okuser)
464{
465 struct stat64 st;
466 char *cp = NULL;
467 FILE *res = NULL;
468
469 /* If not a regular file, if owned by someone other than user or
470 root, if writeable by anyone but the owner, or if hardlinked
471 anywhere, quit. */
472 if (__lxstat64 (_STAT_VER, file, &st))
473 cp = _("lstat failed");
474 else if (!S_ISREG (st.st_mode))
475 cp = _("not regular file");
476 else
477 {
478 res = fopen (file, "rce");
479 if (!res)
480 cp = _("cannot open");
481 else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
482 cp = _("fstat failed");
483 else if (st.st_uid && st.st_uid != okuser)
484 cp = _("bad owner");
485 else if (st.st_mode & (S_IWGRP|S_IWOTH))
486 cp = _("writeable by other than owner");
487 else if (st.st_nlink > 1)
488 cp = _("hard linked somewhere");
489 }
490
491 /* If there were any problems, quit. */
492 if (cp != NULL)
493 {
494 __rcmd_errstr = cp;
495 if (res)
496 fclose (res);
497 return NULL;
498 }
499
500 /* No threads use this stream. */
501 __fsetlocking (res, FSETLOCKING_BYCALLER);
502
503 return res;
504}
505
506/*
507 * New .rhosts strategy: We are passed an ip address. We spin through
508 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
509 * has ip addresses, we don't have to trust a nameserver. When it
510 * contains hostnames, we spin through the list of addresses the nameserver
511 * gives us and look for a match.
512 *
513 * Returns 0 if ok, -1 if not ok.
514 */
515static int
516ruserok2_sa (struct sockaddr *ra, size_t ralen, int superuser,
517 const char *ruser, const char *luser, const char *rhost)
518{
519 FILE *hostf = NULL;
520 int isbad = -1;
521
522 if (!superuser)
523 hostf = iruserfopen (_PATH_HEQUIV, 0);
524
525 if (hostf)
526 {
527 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
528 fclose (hostf);
529
530 if (!isbad)
531 return 0;
532 }
533
534 if (__check_rhosts_file || superuser)
535 {
536 char *pbuf;
537 struct passwd pwdbuf, *pwd;
538 size_t dirlen;
539 size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
540 char *buffer = __alloca (buflen);
541 uid_t uid;
542
543 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
544 || pwd == NULL)
545 return -1;
546
547 dirlen = strlen (pwd->pw_dir);
548 pbuf = alloca (dirlen + sizeof "/.rhosts");
549 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
550 "/.rhosts", sizeof "/.rhosts");
551
552 /* Change effective uid while reading .rhosts. If root and
553 reading an NFS mounted file system, can't read files that
554 are protected read/write owner only. */
555 uid = __geteuid ();
556 seteuid (pwd->pw_uid);
557 hostf = iruserfopen (pbuf, pwd->pw_uid);
558
559 if (hostf != NULL)
560 {
561 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
562 fclose (hostf);
563 }
564
565 seteuid (uid);
566 return isbad;
567 }
568 return -1;
569}
570/*
571 * ruserok_sa() is now discussed on ipng, so
572 * currently disabled for external use
573 */
574static int
575ruserok_sa (struct sockaddr *ra, size_t ralen, int superuser,
576 const char *ruser, const char *luser)
577{
578 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
579}
580
581/* This is the exported version. */
582int
583iruserok_af (const void *raddr, int superuser, const char *ruser,
584 const char *luser, sa_family_t af)
585{
586 union {
587 struct sockaddr generic;
588 struct sockaddr_in in;
589 struct sockaddr_in6 in6;
590 } ra;
591 size_t ralen;
592
593 memset (&ra, '\0', sizeof(ra));
594 switch (af){
595 case AF_INET:
596 ra.in.sin_family = AF_INET;
597 memcpy (&ra.in.sin_addr, raddr, sizeof(struct in_addr));
598 ralen = sizeof(struct sockaddr_in);
599 break;
600 case AF_INET6:
601 ra.in6.sin6_family = AF_INET6;
602 memcpy (&ra.in6.sin6_addr, raddr, sizeof(struct in6_addr));
603 ralen = sizeof(struct sockaddr_in6);
604 break;
605 default:
606 return 0;
607 }
608 return ruserok_sa (&ra.generic, ralen, superuser, ruser, luser);
609}
610libc_hidden_def (iruserok_af)
611
612int
613iruserok (u_int32_t raddr, int superuser, const char *ruser, const char *luser)
614{
615 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
616}
617
618/*
619 * XXX
620 * Don't make static, used by lpd(8).
621 *
622 * This function is not used anymore. It is only present because lpd(8)
623 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
624 * argument. This means that netgroups won't work in .rhost/hosts.equiv
625 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
626 * or PAM.
627 * Returns 0 if ok, -1 if not ok.
628 */
629int
630__ivaliduser (FILE *hostf, u_int32_t raddr, const char *luser,
631 const char *ruser)
632{
633 struct sockaddr_in ra;
634 memset(&ra, '\0', sizeof(ra));
635 ra.sin_family = AF_INET;
636 ra.sin_addr.s_addr = raddr;
637 return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
638 luser, ruser, "-");
639}
640
641
642/* Returns 1 on positive match, 0 on no match, -1 on negative match. */
643static int
644internal_function
645__checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
646 const char *rhost)
647{
648 struct addrinfo hints, *res0, *res;
649 char raddr[INET6_ADDRSTRLEN];
650 int match;
651 int negate=1; /* Multiply return with this to get -1 instead of 1 */
652
653 /* Check nis netgroup. */
654 if (strncmp ("+@", lhost, 2) == 0)
655 return innetgr (&lhost[2], rhost, NULL, NULL);
656
657 if (strncmp ("-@", lhost, 2) == 0)
658 return -innetgr (&lhost[2], rhost, NULL, NULL);
659
660 /* -host */
661 if (strncmp ("-", lhost,1) == 0) {
662 negate = -1;
663 lhost++;
664 } else if (strcmp ("+",lhost) == 0) {
665 return 1; /* asking for trouble, but ok.. */
666 }
667
668 /* Try for raw ip address first. */
669 /* XXX */
670 if (getnameinfo(ra, ralen,
671 raddr, sizeof(raddr), NULL, 0,
672 NI_NUMERICHOST) == 0
673 && strcmp(raddr, lhost) == 0)
674 return negate;
675
676 /* Better be a hostname. */
677 match = 0;
678 memset(&hints, '\0', sizeof(hints));
679 hints.ai_family = ra->sa_family;
680 if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
681 /* Spin through ip addresses. */
682 for (res = res0; res; res = res->ai_next)
683 {
684 if (res->ai_family == ra->sa_family
685 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
686 {
687 match = 1;
688 break;
689 }
690 }
691 freeaddrinfo (res0);
692 }
693 return negate * match;
694}
695
696/* Returns 1 on positive match, 0 on no match, -1 on negative match. */
697static int
698internal_function
699__icheckuser (const char *luser, const char *ruser)
700{
701 /*
702 luser is user entry from .rhosts/hosts.equiv file
703 ruser is user id on remote host
704 */
705
706 /* [-+]@netgroup */
707 if (strncmp ("+@", luser, 2) == 0)
708 return innetgr (&luser[2], NULL, ruser, NULL);
709
710 if (strncmp ("-@", luser,2) == 0)
711 return -innetgr (&luser[2], NULL, ruser, NULL);
712
713 /* -user */
714 if (strncmp ("-", luser, 1) == 0)
715 return -(strcmp (&luser[1], ruser) == 0);
716
717 /* + */
718 if (strcmp ("+", luser) == 0)
719 return 1;
720
721 /* simple string match */
722 return strcmp (ruser, luser) == 0;
723}
724
725/*
726 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
727 */
728static int
729__isempty (char *p)
730{
731 while (*p && isspace (*p)) {
732 ++p;
733 }
734
735 return (*p == '\0' || *p == '#') ? 1 : 0 ;
736}
737
738/*
739 * Returns 0 if positive match, -1 if _not_ ok.
740 */
741static int
742__validuser2_sa (FILE *hostf, struct sockaddr *ra, size_t ralen,
743 const char *luser, const char *ruser, const char *rhost)
744{
745 const char *user;
746 char *p;
747 int hcheck, ucheck;
748 char *buf = NULL;
749 size_t bufsize = 0;
750 int retval = -1;
751
752 while (__getline (&buf, &bufsize, hostf) > 0) {
753 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
754 p = buf;
755
756 /* Skip empty or comment lines */
757 if (__isempty (p)) {
758 continue;
759 }
760
761 for (;*p && !isspace(*p); ++p) {
762 *p = _tolower (*p);
763 }
764
765 /* Next we want to find the permitted name for the remote user. */
766 if (*p == ' ' || *p == '\t') {
767 /* <nul> terminate hostname and skip spaces */
768 for (*p++='\0'; *p && isspace (*p); ++p);
769
770 user = p; /* this is the user's name */
771 while (*p && !isspace (*p))
772 ++p; /* find end of user's name */
773 } else
774 user = p;
775
776 *p = '\0'; /* <nul> terminate username (+host?) */
777
778 /* buf -> host(?) ; user -> username(?) */
779 if (*buf == '\0')
780 break;
781 if (*user == '\0')
782 user = luser;
783
784 /* First check the user part. In a naive implementation we
785 would check the host part first, then the user. However,
786 if we check the user first and reject the entry we will
787 have saved doing any host lookups to normalize the comparison
788 and that likely saves several DNS queries. Therefore we
789 check the user first. */
790 ucheck = __icheckuser (user, ruser);
791
792 /* Either we found the user, or we didn't and this is a
793 negative host check. We must do the negative host lookup
794 in order to preserve the semantics of stopping on this line
795 before processing others. */
796 if (ucheck != 0 || *buf == '-') {
797
798 /* Next check host part. */
799 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
800
801 /* Negative '-host user(?)' match? */
802 if (hcheck < 0)
803 break;
804
805 /* Positive 'host user' match? */
806 if (hcheck > 0 && ucheck > 0) {
807 retval = 0;
808 break;
809 }
810
811 /* Negative 'host -user' match? */
812 if (hcheck > 0 && ucheck < 0)
813 break;
814
815 /* Neither, go on looking for match. */
816 }
817 }
818
819 free (buf);
820
821 return retval;
822}
823