385ce38bd65f8c688c1aba02fce32750a624a125
5 * (c) 2012 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Yet Another Ident Daemon (YAID).
12 * YAID is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * YAID is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with YAID; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 /*----- Header files ------------------------------------------------------*/
31 /*----- Data structures ---------------------------------------------------*/
43 void (*func
)(int, void *);
45 unsigned char buf
[WRBUFSZ
];
66 /*----- Static variables --------------------------------------------------*/
70 static policy_v policy
= DA_INIT
;
73 static unsigned char tokenbuf
[4096];
74 static size_t tokenptr
= sizeof(tokenbuf
);
77 /*----- Main code ---------------------------------------------------------*/
79 static void socket_to_sockaddr(int af
, const struct socket
*s
,
80 struct sockaddr
*sa
, size_t *ssz
)
85 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
86 sin
->sin_addr
= s
->addr
.ipv4
;
87 sin
->sin_port
= htons(s
->port
);
91 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
92 sin6
->sin6_addr
= s
->addr
.ipv6
;
93 sin6
->sin6_port
= htons(s
->port
);
94 sin6
->sin6_flowinfo
= 0;
95 sin6
->sin6_scope_id
= 0;
102 static void sockaddr_to_addr(const struct sockaddr
*sa
, union addr
*a
)
104 switch (sa
->sa_family
) {
105 case AF_INET
: a
->ipv4
= ((struct sockaddr_in
*)sa
)->sin_addr
; break;
106 case AF_INET6
: a
->ipv6
= ((struct sockaddr_in6
*)sa
)->sin6_addr
; break;
111 static void dputsock(dstr
*d
, int af
, const struct socket
*s
)
115 inet_ntop(af
, &s
->addr
, buf
, sizeof(buf
));
116 if (!s
->port
|| af
!= AF_INET6
) dstr_puts(d
, buf
);
117 else { dstr_putc(d
, '['); dstr_puts(d
, buf
); dstr_putc(d
, ']'); }
118 if (s
->port
) dstr_putf(d
, ":%d", s
->port
);
121 void logmsg(const struct query
*q
, int prio
, const char *msg
, ...)
128 dputsock(&d
, q
->af
, &q
->s
[L
]);
129 dstr_puts(&d
, " <-> ");
130 dputsock(&d
, q
->af
, &q
->s
[R
]);
133 dstr_vputf(&d
, msg
, &ap
);
135 fprintf(stderr
, "yaid: %s\n", d
.buf
);
139 static void write_out(int fd
, unsigned mode
, void *p
)
142 struct writebuf
*wb
= p
;
144 if ((n
= write(fd
, wb
->buf
+ wb
->o
, wb
->n
)) < 0) {
145 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) return;
148 wb
->func(errno
, wb
->p
);
159 static int queue_write(struct writebuf
*wb
, const void *p
, size_t n
)
162 if (wb
->n
- wb
->o
+ n
> WRBUFSZ
) return (-1);
164 memmove(wb
->buf
, wb
->buf
+ wb
->o
, wb
->n
);
167 memcpy(wb
->buf
+ wb
->n
, p
, n
);
169 sel_addfile(&wb
->wr
);
176 static void free_writebuf(struct writebuf
*wb
)
177 { if (wb
->n
) sel_rmfile(&wb
->wr
); }
179 static void init_writebuf(struct writebuf
*wb
,
180 int fd
, void (*func
)(int, void *), void *p
)
182 sel_initfile(&sel
, &wb
->wr
, fd
, SEL_WRITE
, write_out
, wb
);
188 static void cancel_proxy(struct proxy
*px
)
194 selbuf_destroy(&px
->b
);
195 free_writebuf(&px
->wb
);
197 selbuf_enable(&px
->c
->b
);
202 static void disconnect_client(struct client
*c
)
205 selbuf_destroy(&c
->b
);
206 free_writebuf(&c
->wb
);
207 if (c
->px
) cancel_proxy(c
->px
);
211 static void done_client_write(int err
, void *p
)
213 struct client
*c
= p
;
216 selbuf_enable(&c
->b
);
218 logmsg(&c
->q
, LOG_ERR
, "failed to send reply: %s", strerror(err
));
219 disconnect_client(c
);
223 static void write_to_client(struct client
*c
, const char *fmt
, ...)
230 n
= vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
232 logmsg(&c
->q
, LOG_ERR
, "failed to format output: %s", strerror(errno
));
233 disconnect_client(c
);
235 } else if (n
> sizeof(buf
)) {
236 logmsg(&c
->q
, LOG_ERR
, "output too long for client send buffer");
237 disconnect_client(c
);
241 selbuf_disable(&c
->b
);
242 if (queue_write(&c
->wb
, buf
, n
)) {
243 logmsg(&c
->q
, LOG_ERR
, "write buffer overflow");
244 disconnect_client(c
);
248 static void reply(struct client
*c
, const char *ty
, const char *msg
)
250 write_to_client(c
, "%u,%u:%s:%s\r\n",
251 c
->q
.s
[L
].port
, c
->q
.s
[R
].port
, ty
, msg
);
254 static void reply_error(struct client
*c
, unsigned err
)
256 assert(err
< E_LIMIT
);
257 reply(c
, "ERROR", errtok
[err
]);
260 static void skipws(const char **pp
)
261 { while (isspace((unsigned char )**pp
)) (*pp
)++; }
263 static int idtoken(const char **pp
, char *q
, size_t n
)
270 if (*p
== ':' || *p
<= 32 || *p
>= 127) break;
280 static int unum(const char **pp
, unsigned *ii
, unsigned min
, unsigned max
)
287 if (!isdigit((unsigned char)**pp
)) return (-1);
288 e
= errno
; errno
= 0;
289 i
= strtoul(*pp
, &q
, 10);
290 if (errno
) return (-1);
293 if (i
< min
|| i
> max
) return (-1);
298 static void proxy_line(char *line
, size_t sz
, void *p
)
300 struct proxy
*px
= p
;
302 const char *q
= line
;
305 while (sz
&& isspace((unsigned char)line
[sz
- 1])) sz
--;
306 printf("received proxy line from %s: %s\n", px
->nat
, line
);
308 if (unum(&q
, &lp
, 1, 65535)) goto syntax
;
309 skipws(&q
); if (*q
!= ',') goto syntax
; q
++;
310 if (unum(&q
, &rp
, 1, 65535)) goto syntax
;
311 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
312 if (lp
!= px
->c
->q
.u
.nat
.port
|| rp
!= px
->c
->q
.s
[R
].port
) goto syntax
;
313 if (idtoken(&q
, buf
, sizeof(buf
))) goto syntax
;
314 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
315 if (strcmp(buf
, "ERROR") == 0) {
317 logmsg(&px
->c
->q
, LOG_ERR
, "proxy error from %s: %s", px
->nat
, q
);
318 reply(px
->c
, "ERROR", q
);
319 } else if (strcmp(buf
, "USERID") == 0) {
320 if (idtoken(&q
, buf
, sizeof(buf
))) goto syntax
;
321 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
323 logmsg(&px
->c
->q
, LOG_ERR
, "user `%s'; proxy = %s, os = %s",
325 write_to_client(px
->c
, "%u,%u:USERID:%s:%s\r\n",
326 px
->c
->q
.s
[L
].port
, px
->c
->q
.s
[R
].port
, buf
, q
);
332 logmsg(&px
->c
->q
, LOG_ERR
, "failed to parse response from %s", px
->nat
);
333 reply_error(px
->c
, E_UNKNOWN
);
338 static void done_proxy_write(int err
, void *p
)
340 struct proxy
*px
= p
;
343 logmsg(&px
->c
->q
, LOG_ERR
, "failed to proxy query to %s: %s",
344 px
->nat
, strerror(errno
));
345 reply_error(px
->c
, E_UNKNOWN
);
349 selbuf_enable(&px
->b
);
352 static void proxy_connected(int fd
, void *p
)
354 struct proxy
*px
= p
;
359 logmsg(&px
->c
->q
, LOG_ERR
,
360 "failed to make %s proxy connection to %s: %s",
361 px
->c
->l
->proto
, px
->nat
, strerror(errno
));
362 reply_error(px
->c
, E_UNKNOWN
);
368 selbuf_init(&px
->b
, &sel
, fd
, proxy_line
, px
);
369 selbuf_setsize(&px
->b
, 1024);
370 selbuf_disable(&px
->b
);
371 init_writebuf(&px
->wb
, fd
, done_proxy_write
, px
);
373 n
= sprintf(buf
, "%u,%u\r\n", px
->c
->q
.u
.nat
.port
, px
->c
->q
.s
[R
].port
);
374 queue_write(&px
->wb
, buf
, n
);
377 static void proxy_query(struct client
*c
)
380 struct sockaddr_storage ss
;
386 px
= xmalloc(sizeof(*px
));
387 inet_ntop(c
->q
.af
, &c
->q
.u
.nat
.addr
, px
->nat
, sizeof(px
->nat
));
389 if ((fd
= socket(c
->q
.af
, SOCK_STREAM
, 0)) < 0) {
390 logmsg(&c
->q
, LOG_ERR
, "failed to make %s socket for proxy: %s",
391 c
->l
->proto
, strerror(errno
));
395 if ((o
= fcntl(fd
, F_GETFL
)) < 0 ||
396 fcntl(fd
, F_SETFL
, o
| O_NONBLOCK
)) {
397 logmsg(&c
->q
, LOG_ERR
, "failed to set %s proxy socket nonblocking: %s",
398 c
->l
->proto
, strerror(errno
));
404 socket_to_sockaddr(c
->q
.af
, &s
, (struct sockaddr
*)&ss
, &ssz
);
405 selbuf_disable(&c
->b
);
406 if (conn_init(&px
->cn
, &sel
, fd
, (struct sockaddr
*)&ss
, ssz
,
407 proxy_connected
, px
)) {
408 logmsg(&c
->q
, LOG_ERR
, "failed to make %s proxy connection to %s: %s",
409 c
->l
->proto
, px
->nat
, strerror(errno
));
413 c
->px
= px
; px
->c
= c
;
418 selbuf_enable(&c
->b
);
423 reply_error(c
, E_UNKNOWN
);
426 static const struct policy default_policy
= POLICY_INIT(A_NAME
);
428 static void user_token(char *p
)
430 static const char tokmap
[64] =
431 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-";
437 if (tokenptr
+ TOKENSZ
>= sizeof(tokenbuf
)) {
438 if (read(randfd
, tokenbuf
, sizeof(tokenbuf
)) < sizeof(tokenbuf
))
439 die(1, "unexpected short read or error from `/dev/urandom'");
443 for (i
= 0; i
< TOKENSZ
; i
++) {
444 a
= (a
<< 8) | tokenbuf
[tokenptr
++]; b
+= 8;
447 *p
++ = tokmap
[(a
>> b
) & 0x3f];
451 *p
++ = tokmap
[(a
<< (6 - b
)) & 0x3f];
455 static void client_line(char *line
, size_t len
, void *p
)
457 struct client
*c
= p
;
459 struct passwd
*pw
= 0;
460 const struct policy
*pol
;
462 struct policy upol
= POLICY_INIT(A_LIMIT
);
463 struct policy_file pf
;
467 c
->q
.s
[L
].port
= c
->q
.s
[R
].port
= 0;
469 disconnect_client(c
);
473 if (fwatch_update(&polfw
, "yaid.policy")) {
474 logmsg(0, LOG_INFO
, "reload master policy file `%s'", "yaid.policy");
475 load_policy_file("yaid.policy", &policy
);
479 if (unum(&q
, &c
->q
.s
[L
].port
, 1, 65535)) goto bad
;
480 skipws(&q
); if (*q
!= ',') goto bad
; q
++;
481 if (unum(&q
, &c
->q
.s
[R
].port
, 1, 65535)) goto bad
;
482 skipws(&q
); if (*q
) goto bad
;
487 if ((pw
= getpwuid(c
->q
.u
.uid
)) == 0) {
488 logmsg(&c
->q
, LOG_ERR
, "no passwd entry for user %d", c
->q
.u
.uid
);
489 reply_error(c
, E_NOUSER
);
497 /* Should already be logged. */
498 reply_error(c
, c
->q
.u
.error
);
504 for (i
= 0; i
< DA_LEN(&policy
); i
++) {
505 pol
= &DA(&policy
)[i
];
506 if (!match_policy(pol
, &c
->q
)) continue;
507 if (pol
->act
.act
!= A_USER
)
510 dstr_putf(&d
, "%s/.yaid.policy", pw
->pw_dir
);
511 if (open_policy_file(&pf
, d
.buf
, "user policy file", &c
->q
))
513 while (!read_policy_file(&pf
)) {
515 logmsg(&c
->q
, LOG_ERR
, "%s:%d: user policy file too long",
519 if (!match_policy(&pf
.p
, &c
->q
)) continue;
520 if (!(pol
->act
.u
.user
& (1 << pf
.p
.act
.act
))) {
521 logmsg(&c
->q
, LOG_ERR
,
522 "%s:%d: user action forbidden by global policy",
526 upol
= pf
.p
; pol
= &upol
;
528 close_policy_file(&pf
);
531 close_policy_file(&pf
);
533 pol
= &default_policy
;
537 switch (pol
->act
.act
) {
539 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d)", pw
->pw_name
, c
->q
.u
.uid
);
540 reply(c
, "USERID:UNIX", pw
->pw_name
);
544 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); token = %s",
545 pw
->pw_name
, c
->q
.u
.uid
, buf
);
546 reply(c
, "USERID:OTHER", buf
);
549 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); denying",
550 pw
->pw_name
, c
->q
.u
.uid
);
553 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); hiding",
554 pw
->pw_name
, c
->q
.u
.uid
);
555 reply_error(c
, E_HIDDEN
);
558 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); lie = `%s'",
559 pw
->pw_name
, c
->q
.u
.uid
, pol
->act
.u
.lie
);
560 reply(c
, "USERID:UNIX", pol
->act
.u
.lie
);
570 logmsg(&c
->q
, LOG_ERR
, "failed to parse query from client");
571 disconnect_client(c
);
574 static void accept_client(int fd
, unsigned mode
, void *p
)
576 struct listen
*l
= p
;
578 struct sockaddr_storage ssr
, ssl
;
579 size_t ssz
= sizeof(ssr
);
582 if ((sk
= accept(fd
, (struct sockaddr
*)&ssr
, &ssz
)) < 0) {
583 if (errno
!= EAGAIN
&& errno
== EWOULDBLOCK
) {
584 logmsg(0, LOG_ERR
, "failed to accept incoming %s connection: %s",
585 l
->proto
, strerror(errno
));
590 c
= xmalloc(sizeof(*c
));
593 sockaddr_to_addr((struct sockaddr
*)&ssr
, &c
->q
.s
[R
].addr
);
595 if (getsockname(sk
, (struct sockaddr
*)&ssl
, &ssz
)) {
597 "failed to read local address for incoming %s connection: %s",
598 l
->proto
, strerror(errno
));
603 sockaddr_to_addr((struct sockaddr
*)&ssl
, &c
->q
.s
[L
].addr
);
604 c
->q
.s
[L
].port
= c
->q
.s
[R
].port
= 0;
606 /* logmsg(&c->q, LOG_INFO, "accepted %s connection", l->proto); */
608 selbuf_init(&c
->b
, &sel
, sk
, client_line
, c
);
609 selbuf_setsize(&c
->b
, 1024);
612 init_writebuf(&c
->wb
, sk
, done_client_write
, c
);
615 static int make_listening_socket(int af
, int port
, const char *proto
)
619 struct sockaddr_storage ss
;
623 if ((fd
= socket(af
, SOCK_STREAM
, 0)) < 0) {
624 die(1, "failed to create %s listening socket: %s",
625 proto
, strerror(errno
));
627 o
= 1; setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &o
, sizeof(o
));
631 struct sockaddr_in
*sin
= (struct sockaddr_in
*)&ss
;
632 sin
->sin_addr
.s_addr
= INADDR_ANY
;
633 sin
->sin_port
= htons(port
);
637 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)&ss
;
638 o
= 1; setsockopt(fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &o
, sizeof(o
));
639 sin6
->sin6_family
= AF_INET6
;
640 sin6
->sin6_addr
= in6addr_any
;
641 sin6
->sin6_scope_id
= 0;
642 sin6
->sin6_flowinfo
= 0;
648 if (bind(fd
, (struct sockaddr
*)&ss
, ssz
))
649 die(1, "failed to bind %s listening socket: %s", proto
, strerror(errno
));
650 if ((o
= fcntl(fd
, F_GETFL
)) < 0 ||
651 fcntl(fd
, F_SETFL
, o
| O_NONBLOCK
)) {
652 die(1, "failed to set %s listening socket nonblocking: %s",
653 proto
, strerror(errno
));
656 die(1, "failed to listen for %s: %s", proto
, strerror(errno
));
658 l
= xmalloc(sizeof(*l
));
661 sel_initfile(&sel
, &l
->f
, fd
, SEL_READ
, accept_client
, l
);
667 int main(int argc
, char *argv
[])
675 fwatch_init(&polfw
, "yaid.policy");
676 if (load_policy_file("yaid.policy", &policy
))
679 for (i
= 0; i
< DA_LEN(&policy
); i
++)
680 print_policy(&DA(&policy
)[i
]);
683 if ((randfd
= open("/dev/urandom", O_RDONLY
)) < 0) {
684 die(1, "failed to open `/dev/urandom' for reading: %s",
688 if (get_default_gw(AF_INET
, &a
))
689 printf("ipv4 gw = %s\n", inet_ntop(AF_INET
, &a
, buf
, sizeof(buf
)));
690 if (get_default_gw(AF_INET6
, &a
))
691 printf("ipv6 gw = %s\n", inet_ntop(AF_INET6
, &a
, buf
, sizeof(buf
)));
694 make_listening_socket(AF_INET
, port
, "IPv4");
695 make_listening_socket(AF_INET6
, port
, "IPv6");
698 if (sel_select(&sel
)) die(1, "select failed: %s", strerror(errno
));
703 /*----- That's all, folks -------------------------------------------------*/