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