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 $
27 * $DragonFly: src/lib/libskey/skeyaccess.c,v 1.4 2005/02/28 14:14:08 joerg Exp $
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
35 #include <arpa/inet.h>
43 #include <sys/param.h>
45 #include "pathnames.h"
48 * Token input with one-deep pushback.
50 static char *prev_token
= 0; /* push-back buffer */
51 static char *line_pointer
= NULL
;
52 static char *first_token (char *, int, FILE *);
53 static int line_number
;
54 static void unget_token (char *);
55 static char *get_token (void);
56 static char *need_token (void);
57 static char *need_internet_addr (void);
60 * Various forms of token matching.
62 #define match_host_name(l) match_token((l)->host_name)
63 #define match_port(l) match_token((l)->port)
64 #define match_user(l) match_token((l)->user)
66 static int match_internet_addr (struct login_info
*);
67 static int match_group (struct login_info
*);
68 static int match_token (char *);
69 static int is_internet_addr (char *);
70 static struct addrinfo
*convert_internet_addr (char *);
71 static struct addrinfo
*lookup_internet_addr (char *);
78 #define CONSOLE "console"
81 #define VTY_PREFIX "ttyv"
85 char *host_name
; /* host name */
86 struct addrinfo
*internet_addr
; /* addrinfo chain */
87 char *user
; /* user name */
88 char *port
; /* login port */
91 static int _skeyaccess (FILE *, struct login_info
*);
92 int skeyaccess (char *, char *, char *, char *);
94 /* skeyaccess - find out if UNIX passwords are permitted */
96 int skeyaccess(user
, port
, host
, addr
)
103 struct login_info login_info
;
107 * Assume no restriction on the use of UNIX passwords when the s/key
108 * acces table does not exist.
110 if ((fp
= fopen(_PATH_SKEYACCESS
, "r")) == 0) {
112 fprintf(stderr
, "No file %s, thus no access control\n", _PATH_SKEYACCESS
);
118 * Bundle up the arguments in a structure so we won't have to drag around
119 * boring long argument lists.
121 * Look up the host address when only the name is given. We try to reject
122 * addresses that belong to someone else.
124 login_info
.user
= user
;
125 login_info
.port
= port
;
127 if (host
!= NULL
&& !is_internet_addr(host
)) {
128 login_info
.host_name
= host
;
130 login_info
.host_name
= NULL
;
133 if (addr
!= NULL
&& is_internet_addr(addr
)) {
134 login_info
.internet_addr
= convert_internet_addr(addr
);
135 } else if (host
!= NULL
) {
136 if (is_internet_addr(host
)) {
137 login_info
.internet_addr
= convert_internet_addr(host
);
139 login_info
.internet_addr
= lookup_internet_addr(host
);
142 login_info
.internet_addr
= NULL
;
146 * Print what we think the user wants us to do.
149 printf("port: %s\n", login_info
.port
);
150 printf("user: %s\n", login_info
.user
);
151 printf("host: %s\n", login_info
.host_name
? login_info
.host_name
: "none");
153 if (login_info
.internet_addr
== NULL
) {
156 struct addrinfo
*res
;
157 char haddr
[NI_MAXHOST
];
159 for (res
= login_info
.internet_addr
; res
; res
= res
->ai_next
) {
160 getnameinfo(res
->ai_addr
, res
->ai_addrlen
, haddr
, sizeof(haddr
),
161 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
162 printf("%s%s", haddr
, res
->ai_next
? " " : "\n");
166 result
= _skeyaccess(fp
, &login_info
);
168 if (login_info
.internet_addr
)
169 freeaddrinfo(login_info
.internet_addr
);
173 /* _skeyaccess - find out if UNIX passwords are permitted */
175 static int _skeyaccess(fp
, login_info
)
177 struct login_info
*login_info
;
184 #ifdef PERMIT_CONSOLE
185 if (login_info
->port
!= 0 &&
186 (strcmp(login_info
->port
, CONSOLE
) == 0 ||
187 strncmp(login_info
->port
, VTY_PREFIX
, sizeof(VTY_PREFIX
) - 1) == 0
194 * Scan the s/key access table until we find an entry that matches. If no
195 * match is found, assume that UNIX passwords are disallowed.
198 while (match
== 0 && (tok
= first_token(buf
, sizeof(buf
), fp
))) {
199 if (strncasecmp(tok
, "permit", 4) == 0) {
201 } else if (strncasecmp(tok
, "deny", 4) == 0) {
204 syslog(LOG_ERR
, "%s: line %d: bad permission: %s",
205 _PATH_SKEYACCESS
, line_number
, tok
);
206 continue; /* error */
210 * Process all conditions in this entry until we find one that fails.
213 while (match
!= 0 && (tok
= get_token())) {
214 if (strcasecmp(tok
, "hostname") == 0) {
215 match
= match_host_name(login_info
);
216 } else if (strcasecmp(tok
, "port") == 0) {
217 match
= match_port(login_info
);
218 } else if (strcasecmp(tok
, "user") == 0) {
219 match
= match_user(login_info
);
220 } else if (strcasecmp(tok
, "group") == 0) {
221 match
= match_group(login_info
);
222 } else if (strcasecmp(tok
, "internet") == 0) {
223 match
= match_internet_addr(login_info
);
224 } else if (is_internet_addr(tok
)) {
226 match
= match_internet_addr(login_info
);
228 syslog(LOG_ERR
, "%s: line %d: bad condition: %s",
229 _PATH_SKEYACCESS
, line_number
, tok
);
234 return (match
? permission
: DENY
);
237 /* translate IPv4 mapped IPv6 address to IPv4 address */
240 ai_unmapped(struct addrinfo
*ai
)
242 struct sockaddr_in6
*sin6
;
243 struct sockaddr_in
*sin4
;
247 if (ai
->ai_family
!= AF_INET6
)
249 sin6
= (struct sockaddr_in6
*)ai
->ai_addr
;
250 if (!IN6_IS_ADDR_V4MAPPED(&sin6
->sin6_addr
))
252 sin4
= (struct sockaddr_in
*)ai
->ai_addr
;
253 addr
= *(u_int32_t
*)&sin6
->sin6_addr
.s6_addr
[12];
254 port
= sin6
->sin6_port
;
255 memset(sin4
, 0, sizeof(struct sockaddr_in
));
256 sin4
->sin_addr
.s_addr
= addr
;
257 sin4
->sin_port
= port
;
258 sin4
->sin_family
= AF_INET
;
259 sin4
->sin_len
= sizeof(struct sockaddr_in
);
260 ai
->ai_family
= AF_INET
;
261 ai
->ai_addrlen
= sizeof(struct sockaddr_in
);
264 /* match_internet_addr - match internet network address */
266 static int match_internet_addr(login_info
)
267 struct login_info
*login_info
;
270 struct addrinfo
*res
;
271 struct sockaddr_storage pattern
, mask
;
272 struct sockaddr_in
*addr4
, *pattern4
, *mask4
;
273 struct sockaddr_in6
*addr6
, *pattern6
, *mask6
;
276 if (login_info
->internet_addr
== NULL
)
278 if ((tok
= need_internet_addr()) == 0)
280 if ((res
= convert_internet_addr(tok
)) == NULL
)
282 memcpy(&pattern
, res
->ai_addr
, res
->ai_addrlen
);
284 if ((tok
= need_internet_addr()) == 0)
286 if ((res
= convert_internet_addr(tok
)) == NULL
)
288 memcpy(&mask
, res
->ai_addr
, res
->ai_addrlen
);
290 if (pattern
.ss_family
!= mask
.ss_family
)
292 mask4
= (struct sockaddr_in
*)&mask
;
293 pattern4
= (struct sockaddr_in
*)&pattern
;
294 mask6
= (struct sockaddr_in6
*)&mask
;
295 pattern6
= (struct sockaddr_in6
*)&pattern
;
298 * See if any of the addresses matches a pattern in the control file. We
299 * have already tried to drop addresses that belong to someone else.
302 for (res
= login_info
->internet_addr
; res
; res
= res
->ai_next
) {
304 if (res
->ai_family
!= pattern
.ss_family
)
306 switch (res
->ai_family
) {
308 addr4
= (struct sockaddr_in
*)res
->ai_addr
;
309 if (addr4
->sin_addr
.s_addr
!= INADDR_NONE
&&
310 (addr4
->sin_addr
.s_addr
& mask4
->sin_addr
.s_addr
) == pattern4
->sin_addr
.s_addr
)
314 addr6
= (struct sockaddr_in6
*)res
->ai_addr
;
315 if (pattern6
->sin6_scope_id
!= 0 &&
316 addr6
->sin6_scope_id
!= pattern6
->sin6_scope_id
)
319 for (i
= 0; i
< 16; ++i
) {
320 if ((addr6
->sin6_addr
.s6_addr
[i
] & mask6
->sin6_addr
.s6_addr
[i
]) != pattern6
->sin6_addr
.s6_addr
[i
]) {
333 /* match_group - match username against group */
335 static int match_group(login_info
)
336 struct login_info
*login_info
;
338 struct passwd
*passwd
;
343 if ((tok
= need_token()) &&
344 (passwd
= getpwnam(login_info
->user
)) && (group
= getgrnam(tok
))) {
345 if (passwd
->pw_gid
== (gid_t
)group
->gr_gid
)
347 for (memp
= group
->gr_mem
; *memp
; memp
++)
348 if (strcmp(login_info
->user
, *memp
) == 0)
351 return (0); /* XXX endgrent() */
354 /* match_token - get and match token */
356 static int match_token(str
)
361 return (str
&& (tok
= need_token()) && strcasecmp(str
, tok
) == 0);
364 /* first_token - read line and return first token */
366 static char *first_token(buf
, len
, fp
)
375 if (fgets(buf
, len
, fp
) == 0)
378 buf
[strcspn(buf
, "\r\n#")] = 0;
381 printf("rule: %s\n", buf
);
384 while ((cp
= strsep(&line_pointer
, " \t")) != NULL
&& *cp
== '\0')
391 /* unget_token - push back last token */
393 static void unget_token(cp
)
399 /* get_token - retrieve next token from buffer */
401 static char *get_token()
405 if ( (cp
= prev_token
) ) {
408 while ((cp
= strsep(&line_pointer
, " \t")) != NULL
&& *cp
== '\0')
414 /* need_token - complain if next token is not available */
416 static char *need_token()
420 if ((cp
= get_token()) == 0)
421 syslog(LOG_ERR
, "%s: line %d: premature end of rule",
422 _PATH_SKEYACCESS
, line_number
);
426 /* need_internet_addr - complain if next token is not an internet address */
428 static char *need_internet_addr()
432 if ((cp
= get_token()) == 0) {
433 syslog(LOG_ERR
, "%s: line %d: internet address expected",
434 _PATH_SKEYACCESS
, line_number
);
436 } else if (!is_internet_addr(cp
)) {
437 syslog(LOG_ERR
, "%s: line %d: bad internet address: %s",
438 _PATH_SKEYACCESS
, line_number
, cp
);
445 /* is_internet_addr - determine if string is a dotted quad decimal address */
447 static int is_internet_addr(str
)
450 struct addrinfo
*res
;
452 if ((res
= convert_internet_addr(str
)) != NULL
)
454 return (res
!= NULL
);
458 * Nuke addrinfo entry from list.
459 * XXX: Depending on the implementation of KAME's getaddrinfo(3).
462 nuke_ai(struct addrinfo
**aip
)
468 if (ai
->ai_canonname
) {
469 if (ai
->ai_next
&& !ai
->ai_next
->ai_canonname
)
470 ai
->ai_next
->ai_canonname
= ai
->ai_canonname
;
472 free(ai
->ai_canonname
);
477 /* lookup_internet_addr - look up internet addresses with extreme prejudice */
479 static struct addrinfo
*
480 lookup_internet_addr(char *host
)
482 struct addrinfo hints
, *res0
, *res
, **resp
;
483 char hname
[NI_MAXHOST
], haddr
[NI_MAXHOST
];
486 memset(&hints
, 0, sizeof(hints
));
487 hints
.ai_family
= PF_UNSPEC
;
488 hints
.ai_socktype
= SOCK_STREAM
;
489 hints
.ai_flags
= AI_PASSIVE
| AI_CANONNAME
;
490 if (getaddrinfo(host
, NULL
, &hints
, &res0
) != 0)
492 if (res0
->ai_canonname
== NULL
) {
498 * Wipe addresses that appear to belong to someone else. We will get
499 * false alarms when when the hostname comes from DNS, while its
500 * addresses are listed under different names in local databases.
502 #define NEQ(x,y) (strcasecmp((x),(y)) != 0)
503 #define NEQ3(x,y,n) (strncasecmp((x),(y), (n)) != 0)
506 while ((res
= *resp
) != NULL
) {
507 if (res
->ai_family
!= AF_INET
&& res
->ai_family
!= AF_INET6
) {
511 error
= getnameinfo(res
->ai_addr
, res
->ai_addrlen
,
512 hname
, sizeof(hname
),
513 NULL
, 0, NI_NAMEREQD
| NI_WITHSCOPEID
);
515 getnameinfo(res
->ai_addr
, res
->ai_addrlen
, haddr
, sizeof(haddr
),
516 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
517 syslog(LOG_ERR
, "address %s not registered for host %s",
518 haddr
, res0
->ai_canonname
);
522 if (NEQ(res0
->ai_canonname
, hname
) &&
523 NEQ3(res0
->ai_canonname
, "localhost.", 10)) {
524 getnameinfo(res
->ai_addr
, res
->ai_addrlen
, haddr
, sizeof(haddr
),
525 NULL
, 0, NI_NUMERICHOST
| NI_WITHSCOPEID
);
526 syslog(LOG_ERR
, "address %s registered for host %s and %s",
527 haddr
, hname
, res0
->ai_canonname
);
531 resp
= &res
->ai_next
;
536 /* convert_internet_addr - convert string to internet address */
538 static struct addrinfo
*convert_internet_addr(string
)
541 struct addrinfo hints
, *res
;
543 memset(&hints
, 0, sizeof(hints
));
544 hints
.ai_family
= PF_UNSPEC
;
545 hints
.ai_socktype
= SOCK_STREAM
;
546 hints
.ai_flags
= AI_PASSIVE
| AI_NUMERICHOST
;
547 if (getaddrinfo(string
, NULL
, &hints
, &res
) != 0)
558 struct addrinfo hints
, *res
;
559 char host
[MAXHOSTNAMELEN
+ 1];
564 if (argc
!= 3 && argc
!= 4) {
565 fprintf(stderr
, "usage: %s user port [host_or_ip_address]\n", argv
[0]);
568 if (_PATH_SKEYACCESS
[0] != '/')
569 printf("Warning: this program uses control file: %s\n", _PATH_SKEYACCESS
);
570 openlog("login", LOG_PID
, LOG_AUTH
);
575 memset(&hints
, 0, sizeof(hints
));
576 hints
.ai_family
= PF_UNSPEC
;
577 hints
.ai_socktype
= SOCK_STREAM
;
578 hints
.ai_flags
= AI_PASSIVE
| AI_CANONNAME
;
579 if (getaddrinfo(argv
[3], NULL
, &hints
, &res
) == 0) {
580 if (res
->ai_canonname
== NULL
)
581 strncpy(host
, argv
[3], MAXHOSTNAMELEN
);
583 strncpy(host
, res
->ai_canonname
, MAXHOSTNAMELEN
);
586 strncpy(host
, argv
[3], MAXHOSTNAMELEN
);
587 host
[MAXHOSTNAMELEN
] = 0;
589 verdict
= skeyaccess(user
, port
, argv
[3] ? host
: (char *) 0, (char *) 0);
590 printf("UNIX passwords %spermitted\n", verdict
? "" : "NOT ");