2 * Figure out if UNIX passwords are permitted for any combination of user
3 * name, group member, terminal port, host_name or network:
5 * Programmatic interface: skeyaccess(user, port, host, addr)
7 * All arguments are null-terminated strings. Specify a null character pointer
8 * where information is not available.
10 * When no address information is given this code performs the host (internet)
11 * address lookup itself. It rejects addresses that appear to belong to
14 * When compiled with -DPERMIT_CONSOLE always permits UNIX passwords with
15 * console logins, no matter what the configuration file says.
17 * To build a stand-alone test version, compile with -DTEST and run it off an
18 * skey.access file in the current directory:
20 * Command-line interface: ./skeyaccess user port [host_or_ip_addr]
22 * Errors are reported via syslogd.
24 * Author: Wietse Venema, Eindhoven University of Technology.
26 * $FreeBSD: src/lib/libskey/skeyaccess.c,v 1.9.6.2 2002/08/12 19:42:24 iedowse Exp $
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
34 #include <arpa/inet.h>
42 #include <sys/param.h>
44 #include "pathnames.h"
47 * Token input with one-deep pushback.
49 static char *prev_token = 0; /* push-back buffer */
50 static char *line_pointer = NULL;
51 static char *first_token __P((char *, int, FILE *));
52 static int line_number;
53 static void unget_token __P((char *));
54 static char *get_token __P((void));
55 static char *need_token __P((void));
56 static char *need_internet_addr __P((void));
59 * Various forms of token matching.
61 #define match_host_name(l) match_token((l)->host_name)
62 #define match_port(l) match_token((l)->port)
63 #define match_user(l) match_token((l)->user)
65 static int match_internet_addr __P((struct login_info *));
66 static int match_group __P((struct login_info *));
67 static int match_token __P((char *));
68 static int is_internet_addr __P((char *));
69 static struct addrinfo *convert_internet_addr __P((char *));
70 static struct addrinfo *lookup_internet_addr __P((char *));
77 #define CONSOLE "console"
80 #define VTY_PREFIX "ttyv"
84 char *host_name; /* host name */
85 struct addrinfo *internet_addr; /* addrinfo chain */
86 char *user; /* user name */
87 char *port; /* login port */
90 static int _skeyaccess __P((FILE *, struct login_info *));
91 int skeyaccess __P((char *, char *, char *, char *));
93 /* skeyaccess - find out if UNIX passwords are permitted */
95 int skeyaccess(user, port, host, addr)
102 struct login_info login_info;
106 * Assume no restriction on the use of UNIX passwords when the s/key
107 * acces table does not exist.
109 if ((fp = fopen(_PATH_SKEYACCESS, "r")) == 0) {
111 fprintf(stderr, "No file %s, thus no access control\n", _PATH_SKEYACCESS);
117 * Bundle up the arguments in a structure so we won't have to drag around
118 * boring long argument lists.
120 * Look up the host address when only the name is given. We try to reject
121 * addresses that belong to someone else.
123 login_info.user = user;
124 login_info.port = port;
126 if (host != NULL && !is_internet_addr(host)) {
127 login_info.host_name = host;
129 login_info.host_name = NULL;
132 if (addr != NULL && is_internet_addr(addr)) {
133 login_info.internet_addr = convert_internet_addr(addr);
134 } else if (host != NULL) {
135 if (is_internet_addr(host)) {
136 login_info.internet_addr = convert_internet_addr(host);
138 login_info.internet_addr = lookup_internet_addr(host);
141 login_info.internet_addr = NULL;
145 * Print what we think the user wants us to do.
148 printf("port: %s\n", login_info.port);
149 printf("user: %s\n", login_info.user);
150 printf("host: %s\n", login_info.host_name ? login_info.host_name : "none");
152 if (login_info.internet_addr == NULL) {
155 struct addrinfo *res;
156 char haddr[NI_MAXHOST];
158 for (res = login_info.internet_addr; res; res = res->ai_next) {
159 getnameinfo(res->ai_addr, res->ai_addrlen, haddr, sizeof(haddr),
160 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
161 printf("%s%s", haddr, res->ai_next ? " " : "\n");
165 result = _skeyaccess(fp, &login_info);
167 if (login_info.internet_addr)
168 freeaddrinfo(login_info.internet_addr);
172 /* _skeyaccess - find out if UNIX passwords are permitted */
174 static int _skeyaccess(fp, login_info)
176 struct login_info *login_info;
183 #ifdef PERMIT_CONSOLE
184 if (login_info->port != 0 &&
185 (strcmp(login_info->port, CONSOLE) == 0 ||
186 strncmp(login_info->port, VTY_PREFIX, sizeof(VTY_PREFIX) - 1) == 0
193 * Scan the s/key access table until we find an entry that matches. If no
194 * match is found, assume that UNIX passwords are disallowed.
197 while (match == 0 && (tok = first_token(buf, sizeof(buf), fp))) {
198 if (strncasecmp(tok, "permit", 4) == 0) {
200 } else if (strncasecmp(tok, "deny", 4) == 0) {
203 syslog(LOG_ERR, "%s: line %d: bad permission: %s",
204 _PATH_SKEYACCESS, line_number, tok);
205 continue; /* error */
209 * Process all conditions in this entry until we find one that fails.
212 while (match != 0 && (tok = get_token())) {
213 if (strcasecmp(tok, "hostname") == 0) {
214 match = match_host_name(login_info);
215 } else if (strcasecmp(tok, "port") == 0) {
216 match = match_port(login_info);
217 } else if (strcasecmp(tok, "user") == 0) {
218 match = match_user(login_info);
219 } else if (strcasecmp(tok, "group") == 0) {
220 match = match_group(login_info);
221 } else if (strcasecmp(tok, "internet") == 0) {
222 match = match_internet_addr(login_info);
223 } else if (is_internet_addr(tok)) {
225 match = match_internet_addr(login_info);
227 syslog(LOG_ERR, "%s: line %d: bad condition: %s",
228 _PATH_SKEYACCESS, line_number, tok);
233 return (match ? permission : DENY);
236 /* translate IPv4 mapped IPv6 address to IPv4 address */
239 ai_unmapped(struct addrinfo *ai)
241 struct sockaddr_in6 *sin6;
242 struct sockaddr_in *sin4;
246 if (ai->ai_family != AF_INET6)
248 sin6 = (struct sockaddr_in6 *)ai->ai_addr;
249 if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
251 sin4 = (struct sockaddr_in *)ai->ai_addr;
252 addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
253 port = sin6->sin6_port;
254 memset(sin4, 0, sizeof(struct sockaddr_in));
255 sin4->sin_addr.s_addr = addr;
256 sin4->sin_port = port;
257 sin4->sin_family = AF_INET;
258 sin4->sin_len = sizeof(struct sockaddr_in);
259 ai->ai_family = AF_INET;
260 ai->ai_addrlen = sizeof(struct sockaddr_in);
263 /* match_internet_addr - match internet network address */
265 static int match_internet_addr(login_info)
266 struct login_info *login_info;
269 struct addrinfo *res;
270 struct sockaddr_storage pattern, mask;
271 struct sockaddr_in *addr4, *pattern4, *mask4;
272 struct sockaddr_in6 *addr6, *pattern6, *mask6;
275 if (login_info->internet_addr == NULL)
277 if ((tok = need_internet_addr()) == 0)
279 if ((res = convert_internet_addr(tok)) == NULL)
281 memcpy(&pattern, res->ai_addr, res->ai_addrlen);
283 if ((tok = need_internet_addr()) == 0)
285 if ((res = convert_internet_addr(tok)) == NULL)
287 memcpy(&mask, res->ai_addr, res->ai_addrlen);
289 if (pattern.ss_family != mask.ss_family)
291 mask4 = (struct sockaddr_in *)&mask;
292 pattern4 = (struct sockaddr_in *)&pattern;
293 mask6 = (struct sockaddr_in6 *)&mask;
294 pattern6 = (struct sockaddr_in6 *)&pattern;
297 * See if any of the addresses matches a pattern in the control file. We
298 * have already tried to drop addresses that belong to someone else.
301 for (res = login_info->internet_addr; res; res = res->ai_next) {
303 if (res->ai_family != pattern.ss_family)
305 switch (res->ai_family) {
307 addr4 = (struct sockaddr_in *)res->ai_addr;
308 if (addr4->sin_addr.s_addr != INADDR_NONE &&
309 (addr4->sin_addr.s_addr & mask4->sin_addr.s_addr) == pattern4->sin_addr.s_addr)
313 addr6 = (struct sockaddr_in6 *)res->ai_addr;
314 if (pattern6->sin6_scope_id != 0 &&
315 addr6->sin6_scope_id != pattern6->sin6_scope_id)
318 for (i = 0; i < 16; ++i) {
319 if ((addr6->sin6_addr.s6_addr[i] & mask6->sin6_addr.s6_addr[i]) != pattern6->sin6_addr.s6_addr[i]) {
332 /* match_group - match username against group */
334 static int match_group(login_info)
335 struct login_info *login_info;
337 struct passwd *passwd;
342 if ((tok = need_token()) &&
343 (passwd = getpwnam(login_info->user)) && (group = getgrnam(tok))) {
344 if (passwd->pw_gid == (gid_t)group->gr_gid)
346 for (memp = group->gr_mem; *memp; memp++)
347 if (strcmp(login_info->user, *memp) == 0)
350 return (0); /* XXX endgrent() */
353 /* match_token - get and match token */
355 static int match_token(str)
360 return (str && (tok = need_token()) && strcasecmp(str, tok) == 0);
363 /* first_token - read line and return first token */
365 static char *first_token(buf, len, fp)
374 if (fgets(buf, len, fp) == 0)
377 buf[strcspn(buf, "\r\n#")] = 0;
380 printf("rule: %s\n", buf);
383 while ((cp = strsep(&line_pointer, " \t")) != NULL && *cp == '\0')
390 /* unget_token - push back last token */
392 static void unget_token(cp)
398 /* get_token - retrieve next token from buffer */
400 static char *get_token()
404 if ( (cp = prev_token) ) {
407 while ((cp = strsep(&line_pointer, " \t")) != NULL && *cp == '\0')
413 /* need_token - complain if next token is not available */
415 static char *need_token()
419 if ((cp = get_token()) == 0)
420 syslog(LOG_ERR, "%s: line %d: premature end of rule",
421 _PATH_SKEYACCESS, line_number);
425 /* need_internet_addr - complain if next token is not an internet address */
427 static char *need_internet_addr()
431 if ((cp = get_token()) == 0) {
432 syslog(LOG_ERR, "%s: line %d: internet address expected",
433 _PATH_SKEYACCESS, line_number);
435 } else if (!is_internet_addr(cp)) {
436 syslog(LOG_ERR, "%s: line %d: bad internet address: %s",
437 _PATH_SKEYACCESS, line_number, cp);
444 /* is_internet_addr - determine if string is a dotted quad decimal address */
446 static int is_internet_addr(str)
449 struct addrinfo *res;
451 if ((res = convert_internet_addr(str)) != NULL)
453 return (res != NULL);
457 * Nuke addrinfo entry from list.
458 * XXX: Depending on the implementation of KAME's getaddrinfo(3).
460 static void nuke_ai(aip)
461 struct addrinfo **aip;
467 if (ai->ai_canonname) {
468 if (ai->ai_next && !ai->ai_next->ai_canonname)
469 ai->ai_next->ai_canonname = ai->ai_canonname;
471 free(ai->ai_canonname);
476 /* lookup_internet_addr - look up internet addresses with extreme prejudice */
478 static struct addrinfo *lookup_internet_addr(host)
481 struct addrinfo hints, *res0, *res, **resp;
482 char hname[NI_MAXHOST], haddr[NI_MAXHOST];
485 memset(&hints, 0, sizeof(hints));
486 hints.ai_family = PF_UNSPEC;
487 hints.ai_socktype = SOCK_STREAM;
488 hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
489 if (getaddrinfo(host, NULL, &hints, &res0) != 0)
491 if (res0->ai_canonname == NULL) {
497 * Wipe addresses that appear to belong to someone else. We will get
498 * false alarms when when the hostname comes from DNS, while its
499 * addresses are listed under different names in local databases.
501 #define NEQ(x,y) (strcasecmp((x),(y)) != 0)
502 #define NEQ3(x,y,n) (strncasecmp((x),(y), (n)) != 0)
505 while ((res = *resp) != NULL) {
506 if (res->ai_family != AF_INET && res->ai_family != AF_INET6) {
510 error = getnameinfo(res->ai_addr, res->ai_addrlen,
511 hname, sizeof(hname),
512 NULL, 0, NI_NAMEREQD | NI_WITHSCOPEID);
514 getnameinfo(res->ai_addr, res->ai_addrlen, haddr, sizeof(haddr),
515 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
516 syslog(LOG_ERR, "address %s not registered for host %s",
517 haddr, res0->ai_canonname);
521 if (NEQ(res0->ai_canonname, hname) &&
522 NEQ3(res0->ai_canonname, "localhost.", 10)) {
523 getnameinfo(res->ai_addr, res->ai_addrlen, haddr, sizeof(haddr),
524 NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
525 syslog(LOG_ERR, "address %s registered for host %s and %s",
526 haddr, hname, res0->ai_canonname);
530 resp = &res->ai_next;
535 /* convert_internet_addr - convert string to internet address */
537 static struct addrinfo *convert_internet_addr(string)
540 struct addrinfo hints, *res;
542 memset(&hints, 0, sizeof(hints));
543 hints.ai_family = PF_UNSPEC;
544 hints.ai_socktype = SOCK_STREAM;
545 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
546 if (getaddrinfo(string, NULL, &hints, &res) != 0)
557 struct addrinfo hints, *res;
558 char host[MAXHOSTNAMELEN + 1];
563 if (argc != 3 && argc != 4) {
564 fprintf(stderr, "usage: %s user port [host_or_ip_address]\n", argv[0]);
567 if (_PATH_SKEYACCESS[0] != '/')
568 printf("Warning: this program uses control file: %s\n", _PATH_SKEYACCESS);
569 openlog("login", LOG_PID, LOG_AUTH);
574 memset(&hints, 0, sizeof(hints));
575 hints.ai_family = PF_UNSPEC;
576 hints.ai_socktype = SOCK_STREAM;
577 hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
578 if (getaddrinfo(argv[3], NULL, &hints, &res) == 0) {
579 if (res->ai_canonname == NULL)
580 strncpy(host, argv[3], MAXHOSTNAMELEN);
582 strncpy(host, res->ai_canonname, MAXHOSTNAMELEN);
585 strncpy(host, argv[3], MAXHOSTNAMELEN);
586 host[MAXHOSTNAMELEN] = 0;
588 verdict = skeyaccess(user, port, argv[3] ? host : (char *) 0, (char *) 0);
589 printf("UNIX passwords %spermitted\n", verdict ? "" : "NOT ");