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