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#if defined(LIBC_SCCS) && !defined(lint)
59static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
60#endif /* LIBC_SCCS and not lint */
61
62#include <sys/param.h>
63#include <sys/poll.h>
64#include <sys/socket.h>
65#include <sys/stat.h>
66
67#include <netinet/in.h>
68#include <arpa/inet.h>
69
70#include <alloca.h>
71#include <signal.h>
72#include <fcntl.h>
73#include <netdb.h>
74#include <unistd.h>
75#include <pwd.h>
76#include <errno.h>
77#include <stdio.h>
78#include <stdio_ext.h>
79#include <ctype.h>
80#include <string.h>
81#include <libintl.h>
82#include <stdlib.h>
83#include <wchar.h>
84#include <sys/uio.h>
85
86
87int __ivaliduser (FILE *, u_int32_t, const char *, const char *);
88static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
89 const char *, const char *, const char *);
90static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
91 int superuser, const char *ruser,
92 const char *luser, const char *rhost);
93static int ruserok_sa (struct sockaddr *ra, size_t ralen,
94 int superuser, const char *ruser,
95 const char *luser);
96int iruserok_af (const void *raddr, int superuser, const char *ruser,
97 const char *luser, sa_family_t af);
98int iruserok (u_int32_t raddr, int superuser, const char *ruser,
99 const char *luser);
100
101libc_hidden_proto (iruserok_af)
102
103libc_freeres_ptr(static char *ahostbuf);
104
105int
106rcmd_af (char **ahost, u_short rport, const char *locuser, const char *remuser,
107 const char *cmd, int *fd2p, sa_family_t af)
108{
109 char paddr[INET6_ADDRSTRLEN];
110 struct addrinfo hints, *res, *ai;
111 union
112 {
113 struct sockaddr sa;
114 struct sockaddr_storage ss;
115 struct sockaddr_in sin;
116 struct sockaddr_in6 sin6;
117 } from;
118 struct pollfd pfd[2];
119 int32_t oldmask;
120 pid_t pid;
121 int s, lport, timo, error;
122 char c;
123 int refused;
124 char num[8];
125 ssize_t n;
126
127 if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
128 {
129 __set_errno (EAFNOSUPPORT);
130 return -1;
131 }
132
133 pid = __getpid();
134
135 memset(&hints, '\0', sizeof(hints));
136 hints.ai_flags = AI_CANONNAME;
137 hints.ai_family = af;
138 hints.ai_socktype = SOCK_STREAM;
139 (void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
140 error = getaddrinfo(*ahost, num, &hints, &res);
141 if (error) {
142 if (error == EAI_NONAME && *ahost != NULL)
143 __fxprintf(NULL, "%s: Unknown host\n", *ahost);
144 else
145 __fxprintf(NULL, "rcmd: getaddrinfo: %s\n",
146 gai_strerror(error));
147
148 return -1;
149 }
150
151 pfd[0].events = POLLIN;
152 pfd[1].events = POLLIN;
153
154 if (res->ai_canonname){
155 free (ahostbuf);
156 ahostbuf = strdup (res->ai_canonname);
157 if (ahostbuf == NULL) {
158 __fxprintf(NULL, "%s",
159 _("rcmd: Cannot allocate memory\n"));
160 return -1;
161 }
162 *ahost = ahostbuf;
163 } else
164 *ahost = NULL;
165 ai = res;
166 refused = 0;
167 oldmask = __sigblock(sigmask(SIGURG));
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 __sigsetmask(oldmask);
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 __sigsetmask(oldmask);
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 __sigsetmask(oldmask);
345 freeaddrinfo(res);
346 return s;
347bad2:
348 if (lport)
349 (void)__close(*fd2p);
350bad:
351 (void)__close(s);
352 __sigsetmask(oldmask);
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 s = __socket(family, SOCK_STREAM, 0);
391 if (s < 0)
392 return -1;
393
394 memset (&ss, '\0', sizeof(ss));
395#ifdef SALEN
396 ss.generic.__ss_len = len;
397#endif
398 ss.generic.sa_family = family;
399
400 /* Ignore invalid values. */
401 if (*alport < IPPORT_RESERVED / 2)
402 *alport = IPPORT_RESERVED / 2;
403 else if (*alport >= IPPORT_RESERVED)
404 *alport = IPPORT_RESERVED - 1;
405
406 int start = *alport;
407 do {
408 *sport = htons((uint16_t) *alport);
409 if (__bind(s, &ss.generic, len) >= 0)
410 return s;
411 if (errno != EADDRINUSE) {
412 (void)__close(s);
413 return -1;
414 }
415 if ((*alport)-- == IPPORT_RESERVED/2)
416 *alport = IPPORT_RESERVED - 1;
417 } while (*alport != start);
418 (void)__close(s);
419 __set_errno (EAGAIN);
420 return -1;
421}
422libc_hidden_def (rresvport_af)
423
424int
425rresvport (int *alport)
426{
427 return rresvport_af(alport, AF_INET);
428}
429
430int __check_rhosts_file = 1;
431char *__rcmd_errstr;
432
433int
434ruserok_af (const char *rhost, int superuser, const char *ruser,
435 const char *luser, sa_family_t af)
436{
437 struct addrinfo hints, *res, *res0;
438 int gai;
439 int ret;
440
441 memset (&hints, '\0', sizeof(hints));
442 hints.ai_family = af;
443 gai = getaddrinfo(rhost, NULL, &hints, &res0);
444 if (gai)
445 return -1;
446 ret = -1;
447 for (res=res0; res; res=res->ai_next)
448 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
449 superuser, ruser, luser, rhost) == 0){
450 ret = 0;
451 break;
452 }
453 freeaddrinfo(res0);
454 return (ret);
455}
456libc_hidden_def (ruserok_af)
457
458int
459ruserok (const char *rhost, int superuser, const char *ruser,
460 const char *luser)
461{
462 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
463}
464
465/* Extremely paranoid file open function. */
466static FILE *
467iruserfopen (const char *file, uid_t okuser)
468{
469 struct stat64 st;
470 char *cp = NULL;
471 FILE *res = NULL;
472
473 /* If not a regular file, if owned by someone other than user or
474 root, if writeable by anyone but the owner, or if hardlinked
475 anywhere, quit. */
476 if (__lxstat64 (_STAT_VER, file, &st))
477 cp = _("lstat failed");
478 else if (!S_ISREG (st.st_mode))
479 cp = _("not regular file");
480 else
481 {
482 res = fopen (file, "rce");
483 if (!res)
484 cp = _("cannot open");
485 else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
486 cp = _("fstat failed");
487 else if (st.st_uid && st.st_uid != okuser)
488 cp = _("bad owner");
489 else if (st.st_mode & (S_IWGRP|S_IWOTH))
490 cp = _("writeable by other than owner");
491 else if (st.st_nlink > 1)
492 cp = _("hard linked somewhere");
493 }
494
495 /* If there were any problems, quit. */
496 if (cp != NULL)
497 {
498 __rcmd_errstr = cp;
499 if (res)
500 fclose (res);
501 return NULL;
502 }
503
504 /* No threads use this stream. */
505 __fsetlocking (res, FSETLOCKING_BYCALLER);
506
507 return res;
508}
509
510/*
511 * New .rhosts strategy: We are passed an ip address. We spin through
512 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
513 * has ip addresses, we don't have to trust a nameserver. When it
514 * contains hostnames, we spin through the list of addresses the nameserver
515 * gives us and look for a match.
516 *
517 * Returns 0 if ok, -1 if not ok.
518 */
519static int
520ruserok2_sa (struct sockaddr *ra, size_t ralen, int superuser,
521 const char *ruser, const char *luser, const char *rhost)
522{
523 FILE *hostf = NULL;
524 int isbad = -1;
525
526 if (!superuser)
527 hostf = iruserfopen (_PATH_HEQUIV, 0);
528
529 if (hostf)
530 {
531 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
532 fclose (hostf);
533
534 if (!isbad)
535 return 0;
536 }
537
538 if (__check_rhosts_file || superuser)
539 {
540 char *pbuf;
541 struct passwd pwdbuf, *pwd;
542 size_t dirlen;
543 size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
544 char *buffer = __alloca (buflen);
545 uid_t uid;
546
547 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
548 || pwd == NULL)
549 return -1;
550
551 dirlen = strlen (pwd->pw_dir);
552 pbuf = alloca (dirlen + sizeof "/.rhosts");
553 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
554 "/.rhosts", sizeof "/.rhosts");
555
556 /* Change effective uid while reading .rhosts. If root and
557 reading an NFS mounted file system, can't read files that
558 are protected read/write owner only. */
559 uid = __geteuid ();
560 seteuid (pwd->pw_uid);
561 hostf = iruserfopen (pbuf, pwd->pw_uid);
562
563 if (hostf != NULL)
564 {
565 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
566 fclose (hostf);
567 }
568
569 seteuid (uid);
570 return isbad;
571 }
572 return -1;
573}
574/*
575 * ruserok_sa() is now discussed on ipng, so
576 * currently disabled for external use
577 */
578static int
579ruserok_sa (struct sockaddr *ra, size_t ralen, int superuser,
580 const char *ruser, const char *luser)
581{
582 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
583}
584
585/* This is the exported version. */
586int
587iruserok_af (const void *raddr, int superuser, const char *ruser,
588 const char *luser, sa_family_t af)
589{
590 union {
591 struct sockaddr generic;
592 struct sockaddr_in in;
593 struct sockaddr_in6 in6;
594 } ra;
595 size_t ralen;
596
597 memset (&ra, '\0', sizeof(ra));
598 switch (af){
599 case AF_INET:
600 ra.in.sin_family = AF_INET;
601 memcpy (&ra.in.sin_addr, raddr, sizeof(struct in_addr));
602 ralen = sizeof(struct sockaddr_in);
603 break;
604 case AF_INET6:
605 ra.in6.sin6_family = AF_INET6;
606 memcpy (&ra.in6.sin6_addr, raddr, sizeof(struct in6_addr));
607 ralen = sizeof(struct sockaddr_in6);
608 break;
609 default:
610 return 0;
611 }
612 return ruserok_sa (&ra.generic, ralen, superuser, ruser, luser);
613}
614libc_hidden_def (iruserok_af)
615
616int
617iruserok (u_int32_t raddr, int superuser, const char *ruser, const char *luser)
618{
619 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
620}
621
622/*
623 * XXX
624 * Don't make static, used by lpd(8).
625 *
626 * This function is not used anymore. It is only present because lpd(8)
627 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
628 * argument. This means that netgroups won't work in .rhost/hosts.equiv
629 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
630 * or PAM.
631 * Returns 0 if ok, -1 if not ok.
632 */
633int
634__ivaliduser (FILE *hostf, u_int32_t raddr, const char *luser,
635 const char *ruser)
636{
637 struct sockaddr_in ra;
638 memset(&ra, '\0', sizeof(ra));
639 ra.sin_family = AF_INET;
640 ra.sin_addr.s_addr = raddr;
641 return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
642 luser, ruser, "-");
643}
644
645
646/* Returns 1 on positive match, 0 on no match, -1 on negative match. */
647static int
648internal_function
649__checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
650 const char *rhost)
651{
652 struct addrinfo hints, *res0, *res;
653 char raddr[INET6_ADDRSTRLEN];
654 int match;
655 int negate=1; /* Multiply return with this to get -1 instead of 1 */
656
657 /* Check nis netgroup. */
658 if (strncmp ("+@", lhost, 2) == 0)
659 return innetgr (&lhost[2], rhost, NULL, NULL);
660
661 if (strncmp ("-@", lhost, 2) == 0)
662 return -innetgr (&lhost[2], rhost, NULL, NULL);
663
664 /* -host */
665 if (strncmp ("-", lhost,1) == 0) {
666 negate = -1;
667 lhost++;
668 } else if (strcmp ("+",lhost) == 0) {
669 return 1; /* asking for trouble, but ok.. */
670 }
671
672 /* Try for raw ip address first. */
673 /* XXX */
674 if (getnameinfo(ra, ralen,
675 raddr, sizeof(raddr), NULL, 0,
676 NI_NUMERICHOST) == 0
677 && strcmp(raddr, lhost) == 0)
678 return negate;
679
680 /* Better be a hostname. */
681 match = 0;
682 memset(&hints, '\0', sizeof(hints));
683 hints.ai_family = ra->sa_family;
684 if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
685 /* Spin through ip addresses. */
686 for (res = res0; res; res = res->ai_next)
687 {
688 if (res->ai_family == ra->sa_family
689 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
690 {
691 match = 1;
692 break;
693 }
694 }
695 freeaddrinfo (res0);
696 }
697 return negate * match;
698}
699
700/* Returns 1 on positive match, 0 on no match, -1 on negative match. */
701static int
702internal_function
703__icheckuser (const char *luser, const char *ruser)
704{
705 /*
706 luser is user entry from .rhosts/hosts.equiv file
707 ruser is user id on remote host
708 */
709
710 /* [-+]@netgroup */
711 if (strncmp ("+@", luser, 2) == 0)
712 return innetgr (&luser[2], NULL, ruser, NULL);
713
714 if (strncmp ("-@", luser,2) == 0)
715 return -innetgr (&luser[2], NULL, ruser, NULL);
716
717 /* -user */
718 if (strncmp ("-", luser, 1) == 0)
719 return -(strcmp (&luser[1], ruser) == 0);
720
721 /* + */
722 if (strcmp ("+", luser) == 0)
723 return 1;
724
725 /* simple string match */
726 return strcmp (ruser, luser) == 0;
727}
728
729/*
730 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
731 */
732static int
733__isempty (char *p)
734{
735 while (*p && isspace (*p)) {
736 ++p;
737 }
738
739 return (*p == '\0' || *p == '#') ? 1 : 0 ;
740}
741
742/*
743 * Returns 0 if positive match, -1 if _not_ ok.
744 */
745static int
746__validuser2_sa (FILE *hostf, struct sockaddr *ra, size_t ralen,
747 const char *luser, const char *ruser, const char *rhost)
748{
749 const char *user;
750 char *p;
751 int hcheck, ucheck;
752 char *buf = NULL;
753 size_t bufsize = 0;
754 int retval = -1;
755
756 while (__getline (&buf, &bufsize, hostf) > 0) {
757 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
758 p = buf;
759
760 /* Skip empty or comment lines */
761 if (__isempty (p)) {
762 continue;
763 }
764
765 for (;*p && !isspace(*p); ++p) {
766 *p = _tolower (*p);
767 }
768
769 /* Next we want to find the permitted name for the remote user. */
770 if (*p == ' ' || *p == '\t') {
771 /* <nul> terminate hostname and skip spaces */
772 for (*p++='\0'; *p && isspace (*p); ++p);
773
774 user = p; /* this is the user's name */
775 while (*p && !isspace (*p))
776 ++p; /* find end of user's name */
777 } else
778 user = p;
779
780 *p = '\0'; /* <nul> terminate username (+host?) */
781
782 /* buf -> host(?) ; user -> username(?) */
783 if (*buf == '\0')
784 break;
785 if (*user == '\0')
786 user = luser;
787
788 /* First check the user part. In a naive implementation we
789 would check the host part first, then the user. However,
790 if we check the user first and reject the entry we will
791 have saved doing any host lookups to normalize the comparison
792 and that likely saves several DNS queries. Therefore we
793 check the user first. */
794 ucheck = __icheckuser (user, ruser);
795
796 /* Either we found the user, or we didn't and this is a
797 negative host check. We must do the negative host lookup
798 in order to preserve the semantics of stopping on this line
799 before processing others. */
800 if (ucheck != 0 || *buf == '-') {
801
802 /* Next check host part. */
803 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
804
805 /* Negative '-host user(?)' match? */
806 if (hcheck < 0)
807 break;
808
809 /* Positive 'host user' match? */
810 if (hcheck > 0 && ucheck > 0) {
811 retval = 0;
812 break;
813 }
814
815 /* Negative 'host -user' match? */
816 if (hcheck > 0 && ucheck < 0)
817 break;
818
819 /* Neither, go on looking for match. */
820 }
821 }
822
823 free (buf);
824
825 return retval;
826}
827