Account for grouping in printf width (bug 30068)
[glibc.git] / inet / rcmd.c
blob2b95fa11d8d762d245e12c909e3ae1fd92045e3d
1 /*
2 * Copyright (C) 1998 WIDE Project.
3 * All rights reserved.
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.
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.
30 * Copyright (c) 1983, 1993, 1994
31 * The Regents of the University of California. All rights reserved.
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.
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.
58 #include <sys/param.h>
59 #include <sys/poll.h>
60 #include <sys/socket.h>
61 #include <sys/stat.h>
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
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>
85 int __ivaliduser (FILE *, uint32_t, const char *, const char *);
86 static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
87 const char *, const char *, const char *);
88 static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
89 int superuser, const char *ruser,
90 const char *luser, const char *rhost);
91 static int ruserok_sa (struct sockaddr *ra, size_t ralen,
92 int superuser, const char *ruser,
93 const char *luser);
94 int iruserok_af (const void *raddr, int superuser, const char *ruser,
95 const char *luser, sa_family_t af);
96 int iruserok (uint32_t raddr, int superuser, const char *ruser,
97 const char *luser);
99 libc_hidden_proto (iruserok_af)
101 libc_freeres_ptr(static char *ahostbuf);
104 rcmd_af (char **ahost, u_short rport, const char *locuser, const char *remuser,
105 const char *cmd, int *fd2p, sa_family_t af)
107 char paddr[INET6_ADDRSTRLEN];
108 struct addrinfo hints, *res, *ai;
109 union
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;
119 pid_t pid;
120 int s, lport, timo, error;
121 char c;
122 int refused;
123 char num[8];
124 ssize_t n;
126 if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
128 __set_errno (EAFNOSUPPORT);
129 return -1;
132 pid = __getpid();
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));
147 return -1;
150 pfd[0].events = POLLIN;
151 pfd[1].events = POLLIN;
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;
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];
173 s = rresvport_af(&lport, ai->ai_family);
174 if (s < 0) {
175 if (errno == EAGAIN)
176 __fxprintf(NULL, "%s", _("\
177 rcmd: socket: All ports in use\n"));
178 else
179 __fxprintf(NULL, "rcmd: socket: %m\n");
181 __sigprocmask (SIG_SETMASK, &omask, 0);
182 freeaddrinfo(res);
183 return -1;
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;
193 if (errno == ECONNREFUSED)
194 refused = 1;
195 if (ai->ai_next != NULL) {
196 int oerrno = errno;
197 char *buf = NULL;
199 getnameinfo(ai->ai_addr, ai->ai_addrlen,
200 paddr, sizeof(paddr),
201 NULL, 0,
202 NI_NUMERICHOST);
204 if (__asprintf (&buf, _("connect to address %s: "),
205 paddr) >= 0)
207 __fxprintf(NULL, "%s", buf);
208 free (buf);
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)
219 __fxprintf (NULL, "%s", buf);
220 free (buf);
222 continue;
224 if (refused && timo <= 16) {
225 (void)__sleep(timo);
226 timo *= 2;
227 ai = res;
228 refused = 0;
229 continue;
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;
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;
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;
253 if (__asprintf (&buf, _("\
254 rcmd: write (setting up stderr): %m\n")) >= 0)
256 __fxprintf(NULL, "%s", buf);
257 free (buf);
259 (void)__close(s2);
260 goto bad;
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;
268 if ((errno != 0
269 && __asprintf(&buf, _("\
270 rcmd: poll (setting up stderr): %m\n")) >= 0)
271 || (errno == 0
272 && __asprintf(&buf, _("\
273 poll: protocol failure in circuit setup\n")) >= 0))
275 __fxprintf (NULL, "%s", buf);
276 free (buf);
278 (void)__close(s2);
279 goto bad;
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;
293 (void)__close(s2);
294 if (s3 < 0) {
295 (void)__fxprintf(NULL, "rcmd: accept: %m\n");
296 lport = 0;
297 goto bad;
299 *fd2p = s3;
301 if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
302 char *buf = NULL;
304 if (__asprintf(&buf, _("\
305 socket: protocol failure in circuit setup\n")) >= 0)
307 __fxprintf (NULL, "%s", buf);
308 free (buf);
310 goto bad2;
313 struct iovec iov[3] =
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 }
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;
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))
333 __fxprintf (NULL, "%s", buf);
334 free (buf);
336 goto bad2;
338 if (c != 0) {
339 while (__read(s, &c, 1) == 1) {
340 (void)__write(STDERR_FILENO, &c, 1);
341 if (c == '\n')
342 break;
344 goto bad2;
346 __sigprocmask (SIG_SETMASK, &omask, 0);
347 freeaddrinfo(res);
348 return s;
349 bad2:
350 if (lport)
351 (void)__close(*fd2p);
352 bad:
353 (void)__close(s);
354 __sigprocmask (SIG_SETMASK, &omask, 0);
355 freeaddrinfo(res);
356 return -1;
358 libc_hidden_def (rcmd_af)
361 rcmd (char **ahost, u_short rport, const char *locuser, const char *remuser,
362 const char *cmd, int *fd2p)
364 return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
368 rresvport_af (int *alport, sa_family_t family)
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;
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;
392 /* NB: No SOCK_CLOEXEC for backwards compatibility. */
393 s = __socket(family, SOCK_STREAM, 0);
394 if (s < 0)
395 return -1;
397 memset (&ss, '\0', sizeof(ss));
398 #ifdef SALEN
399 ss.generic.__ss_len = len;
400 #endif
401 ss.generic.sa_family = family;
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;
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;
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;
425 libc_hidden_def (rresvport_af)
428 rresvport (int *alport)
430 return rresvport_af(alport, AF_INET);
433 int __check_rhosts_file = 1;
434 char *__rcmd_errstr;
437 ruserok_af (const char *rhost, int superuser, const char *ruser,
438 const char *luser, sa_family_t af)
440 struct addrinfo hints, *res, *res0;
441 int gai;
442 int ret;
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;
456 freeaddrinfo(res0);
457 return (ret);
459 libc_hidden_def (ruserok_af)
462 ruserok (const char *rhost, int superuser, const char *ruser,
463 const char *luser)
465 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
468 /* Extremely paranoid file open function. */
469 static FILE *
470 iruserfopen (const char *file, uid_t okuser)
472 struct __stat64_t64 st;
473 char *cp = NULL;
474 FILE *res = NULL;
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
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");
498 /* If there were any problems, quit. */
499 if (cp != NULL)
501 __rcmd_errstr = cp;
502 if (res)
503 fclose (res);
504 return NULL;
507 /* No threads use this stream. */
508 __fsetlocking (res, FSETLOCKING_BYCALLER);
510 return res;
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.
520 * Returns 0 if ok, -1 if not ok.
522 static int
523 ruserok2_sa (struct sockaddr *ra, size_t ralen, int superuser,
524 const char *ruser, const char *luser, const char *rhost)
526 FILE *hostf = NULL;
527 int isbad = -1;
529 if (!superuser)
530 hostf = iruserfopen (_PATH_HEQUIV, 0);
532 if (hostf)
534 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
535 fclose (hostf);
537 if (!isbad)
538 return 0;
541 if (__check_rhosts_file || superuser)
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;
550 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
551 || pwd == NULL)
552 return -1;
554 dirlen = strlen (pwd->pw_dir);
555 pbuf = alloca (dirlen + sizeof "/.rhosts");
556 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
557 "/.rhosts", sizeof "/.rhosts");
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);
566 if (hostf != NULL)
568 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
569 fclose (hostf);
572 seteuid (uid);
573 return isbad;
575 return -1;
578 * ruserok_sa() is now discussed on ipng, so
579 * currently disabled for external use
581 static int
582 ruserok_sa (struct sockaddr *ra, size_t ralen, int superuser,
583 const char *ruser, const char *luser)
585 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
588 /* This is the exported version. */
590 iruserok_af (const void *raddr, int superuser, const char *ruser,
591 const char *luser, sa_family_t af)
593 union {
594 struct sockaddr generic;
595 struct sockaddr_in in;
596 struct sockaddr_in6 in6;
597 } ra;
598 size_t ralen;
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;
615 return ruserok_sa (&ra.generic, ralen, superuser, ruser, luser);
617 libc_hidden_def (iruserok_af)
620 iruserok (uint32_t raddr, int superuser, const char *ruser, const char *luser)
622 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
625 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_37)
626 /* Previously used by lpd. Current lpd versions have their own copy. */
627 int attribute_compat_text_section
628 __ivaliduser (FILE *hostf, uint32_t raddr, const char *luser,
629 const char *ruser)
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, "-");
638 compat_symbol (libc, __ivaliduser, __ivaliduser, GLIBC_2_0);
639 #endif
641 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
642 static int
643 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
644 const char *rhost)
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 */
651 /* Check nis netgroup. */
652 if (strncmp ("+@", lhost, 2) == 0)
653 return innetgr (&lhost[2], rhost, NULL, NULL);
655 if (strncmp ("-@", lhost, 2) == 0)
656 return -innetgr (&lhost[2], rhost, NULL, NULL);
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.. */
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;
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)
682 if (res->ai_family == ra->sa_family
683 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
685 match = 1;
686 break;
689 freeaddrinfo (res0);
691 return negate * match;
694 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
695 static int
696 __icheckuser (const char *luser, const char *ruser)
699 luser is user entry from .rhosts/hosts.equiv file
700 ruser is user id on remote host
703 /* [-+]@netgroup */
704 if (strncmp ("+@", luser, 2) == 0)
705 return innetgr (&luser[2], NULL, ruser, NULL);
707 if (strncmp ("-@", luser,2) == 0)
708 return -innetgr (&luser[2], NULL, ruser, NULL);
710 /* -user */
711 if (strncmp ("-", luser, 1) == 0)
712 return -(strcmp (&luser[1], ruser) == 0);
714 /* + */
715 if (strcmp ("+", luser) == 0)
716 return 1;
718 /* simple string match */
719 return strcmp (ruser, luser) == 0;
723 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
725 static int
726 __isempty (char *p)
728 while (*p && isspace (*p)) {
729 ++p;
732 return (*p == '\0' || *p == '#') ? 1 : 0 ;
736 * Returns 0 if positive match, -1 if _not_ ok.
738 static int
739 __validuser2_sa (FILE *hostf, struct sockaddr *ra, size_t ralen,
740 const char *luser, const char *ruser, const char *rhost)
742 const char *user;
743 char *p;
744 int hcheck, ucheck;
745 char *buf = NULL;
746 size_t bufsize = 0;
747 int retval = -1;
749 while (__getline (&buf, &bufsize, hostf) > 0) {
750 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
751 p = buf;
753 /* Skip empty or comment lines */
754 if (__isempty (p)) {
755 continue;
758 for (;*p && !isspace(*p); ++p) {
759 *p = _tolower (*p);
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);
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;
773 *p = '\0'; /* <nul> terminate username (+host?) */
775 /* buf -> host(?) ; user -> username(?) */
776 if (*buf == '\0')
777 break;
778 if (*user == '\0')
779 user = luser;
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);
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 == '-') {
795 /* Next check host part. */
796 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
798 /* Negative '-host user(?)' match? */
799 if (hcheck < 0)
800 break;
802 /* Positive 'host user' match? */
803 if (hcheck > 0 && ucheck > 0) {
804 retval = 0;
805 break;
808 /* Negative 'host -user' match? */
809 if (hcheck > 0 && ucheck < 0)
810 break;
812 /* Neither, go on looking for match. */
816 free (buf);
818 return retval;