fc6ddd4507c78771063dfec77cdf4cb63f3a4775
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 ---------------------------------------------------*/
34 const struct addrops
*ao
;
42 void (*func
)(int, void *);
44 unsigned char buf
[WRBUFSZ
];
65 /*----- Static variables --------------------------------------------------*/
69 static policy_v policy
= DA_INIT
;
72 static unsigned char tokenbuf
[4096];
73 static size_t tokenptr
= sizeof(tokenbuf
);
76 /*----- Main code ---------------------------------------------------------*/
78 void logmsg(const struct query
*q
, int prio
, const char *msg
, ...)
85 dputsock(&d
, q
->ao
, &q
->s
[L
]);
86 dstr_puts(&d
, " <-> ");
87 dputsock(&d
, q
->ao
, &q
->s
[R
]);
90 dstr_vputf(&d
, msg
, &ap
);
92 fprintf(stderr
, "yaid: %s\n", d
.buf
);
96 static void write_out(int fd
, unsigned mode
, void *p
)
99 struct writebuf
*wb
= p
;
101 if ((n
= write(fd
, wb
->buf
+ wb
->o
, wb
->n
)) < 0) {
102 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) return;
105 wb
->func(errno
, wb
->p
);
116 static int queue_write(struct writebuf
*wb
, const void *p
, size_t n
)
119 if (wb
->n
- wb
->o
+ n
> WRBUFSZ
) return (-1);
121 memmove(wb
->buf
, wb
->buf
+ wb
->o
, wb
->n
);
124 memcpy(wb
->buf
+ wb
->n
, p
, n
);
126 sel_addfile(&wb
->wr
);
133 static void free_writebuf(struct writebuf
*wb
)
134 { if (wb
->n
) sel_rmfile(&wb
->wr
); }
136 static void init_writebuf(struct writebuf
*wb
,
137 int fd
, void (*func
)(int, void *), void *p
)
139 sel_initfile(&sel
, &wb
->wr
, fd
, SEL_WRITE
, write_out
, wb
);
145 static void cancel_proxy(struct proxy
*px
)
151 selbuf_destroy(&px
->b
);
152 free_writebuf(&px
->wb
);
154 selbuf_enable(&px
->c
->b
);
159 static void disconnect_client(struct client
*c
)
162 selbuf_destroy(&c
->b
);
163 free_writebuf(&c
->wb
);
164 if (c
->px
) cancel_proxy(c
->px
);
168 static int fix_up_socket(int fd
, const char *what
)
172 if (fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0)) {
173 logmsg(0, LOG_ERR
, "failed to set %s connection nonblocking: %s",
174 what
, strerror(errno
));
178 if (setsockopt(fd
, SOL_SOCKET
, SO_OOBINLINE
, &yes
, sizeof(yes
))) {
180 "failed to disable `out-of-band' data on %s connection: %s",
181 what
, strerror(errno
));
188 static void done_client_write(int err
, void *p
)
190 struct client
*c
= p
;
193 selbuf_enable(&c
->b
);
195 logmsg(&c
->q
, LOG_ERR
, "failed to send reply: %s", strerror(err
));
196 disconnect_client(c
);
200 static void write_to_client(struct client
*c
, const char *fmt
, ...)
207 n
= vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
209 logmsg(&c
->q
, LOG_ERR
, "failed to format output: %s", strerror(errno
));
210 disconnect_client(c
);
212 } else if (n
> sizeof(buf
)) {
213 logmsg(&c
->q
, LOG_ERR
, "output too long for client send buffer");
214 disconnect_client(c
);
218 selbuf_disable(&c
->b
);
219 if (queue_write(&c
->wb
, buf
, n
)) {
220 logmsg(&c
->q
, LOG_ERR
, "write buffer overflow");
221 disconnect_client(c
);
225 static void reply(struct client
*c
, const char *ty
,
226 const char *tok0
, const char *tok1
)
228 write_to_client(c
, "%u,%u:%s:%s%s%s\r\n",
229 c
->q
.s
[L
].port
, c
->q
.s
[R
].port
, ty
,
230 tok0
, tok1 ?
":" : "", tok1 ? tok1
: "");
233 const char *const errtok
[] = {
234 #define DEFTOK(err, tok) tok,
239 static void reply_error(struct client
*c
, unsigned err
)
241 assert(err
< E_LIMIT
);
242 reply(c
, "ERROR", errtok
[err
], 0);
245 static void skipws(const char **pp
)
246 { while (isspace((unsigned char )**pp
)) (*pp
)++; }
248 static int idtoken(const char **pp
, char *q
, size_t n
)
255 if (*p
== ':' || *p
<= 32 || *p
>= 127) break;
265 static int unum(const char **pp
, unsigned *ii
, unsigned min
, unsigned max
)
272 if (!isdigit((unsigned char)**pp
)) return (-1);
273 e
= errno
; errno
= 0;
274 i
= strtoul(*pp
, &q
, 10);
275 if (errno
) return (-1);
278 if (i
< min
|| i
> max
) return (-1);
283 static void proxy_line(char *line
, size_t sz
, void *p
)
285 struct proxy
*px
= p
;
287 const char *q
= line
;
290 while (sz
&& isspace((unsigned char)line
[sz
- 1])) sz
--;
291 printf("received proxy line from %s: %s\n", px
->nat
, line
);
293 if (unum(&q
, &lp
, 1, 65535)) goto syntax
;
294 skipws(&q
); if (*q
!= ',') goto syntax
; q
++;
295 if (unum(&q
, &rp
, 1, 65535)) goto syntax
;
296 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
297 if (lp
!= px
->c
->q
.u
.nat
.port
|| rp
!= px
->c
->q
.s
[R
].port
) goto syntax
;
298 if (idtoken(&q
, buf
, sizeof(buf
))) goto syntax
;
299 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
300 if (strcmp(buf
, "ERROR") == 0) {
302 logmsg(&px
->c
->q
, LOG_ERR
, "proxy error from %s: %s", px
->nat
, q
);
303 reply(px
->c
, "ERROR", q
, 0);
304 } else if (strcmp(buf
, "USERID") == 0) {
305 if (idtoken(&q
, buf
, sizeof(buf
))) goto syntax
;
306 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
308 logmsg(&px
->c
->q
, LOG_ERR
, "user `%s'; proxy = %s, os = %s",
310 reply(px
->c
, "USERID", buf
, q
);
316 logmsg(&px
->c
->q
, LOG_ERR
, "failed to parse response from %s", px
->nat
);
317 reply_error(px
->c
, E_UNKNOWN
);
322 static void done_proxy_write(int err
, void *p
)
324 struct proxy
*px
= p
;
327 logmsg(&px
->c
->q
, LOG_ERR
, "failed to proxy query to %s: %s",
328 px
->nat
, strerror(errno
));
329 reply_error(px
->c
, E_UNKNOWN
);
333 selbuf_enable(&px
->b
);
336 static void proxy_connected(int fd
, void *p
)
338 struct proxy
*px
= p
;
343 logmsg(&px
->c
->q
, LOG_ERR
,
344 "failed to make %s proxy connection to %s: %s",
345 px
->c
->l
->ao
->name
, px
->nat
, strerror(errno
));
346 reply_error(px
->c
, E_UNKNOWN
);
352 selbuf_init(&px
->b
, &sel
, fd
, proxy_line
, px
);
353 selbuf_setsize(&px
->b
, 1024);
354 selbuf_disable(&px
->b
);
355 init_writebuf(&px
->wb
, fd
, done_proxy_write
, px
);
357 n
= sprintf(buf
, "%u,%u\r\n", px
->c
->q
.u
.nat
.port
, px
->c
->q
.s
[R
].port
);
358 queue_write(&px
->wb
, buf
, n
);
361 static void proxy_query(struct client
*c
)
364 struct sockaddr_storage ss
;
369 px
= xmalloc(sizeof(*px
));
370 inet_ntop(c
->q
.ao
->af
, &c
->q
.u
.nat
.addr
, px
->nat
, sizeof(px
->nat
));
372 if ((fd
= socket(c
->q
.ao
->af
, SOCK_STREAM
, 0)) < 0) {
373 logmsg(&c
->q
, LOG_ERR
, "failed to make %s socket for proxy: %s",
374 c
->l
->ao
->name
, strerror(errno
));
377 if (fix_up_socket(fd
, "proxy")) goto err_1
;
381 c
->l
->ao
->socket_to_sockaddr(&s
, &ss
, &ssz
);
382 selbuf_disable(&c
->b
);
383 c
->px
= px
; px
->c
= c
;
385 if (conn_init(&px
->cn
, &sel
, fd
, (struct sockaddr
*)&ss
, ssz
,
386 proxy_connected
, px
)) {
387 logmsg(&c
->q
, LOG_ERR
, "failed to make %s proxy connection to %s: %s",
388 c
->l
->ao
->name
, px
->nat
, strerror(errno
));
395 selbuf_enable(&c
->b
);
400 reply_error(c
, E_UNKNOWN
);
403 static const struct policy default_policy
= POLICY_INIT(A_NAME
);
405 static void user_token(char *p
)
407 static const char tokmap
[64] =
408 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-";
414 if (tokenptr
+ TOKENSZ
>= sizeof(tokenbuf
)) {
415 if (read(randfd
, tokenbuf
, sizeof(tokenbuf
)) < sizeof(tokenbuf
))
416 die(1, "unexpected short read or error from `/dev/urandom'");
420 for (i
= 0; i
< TOKENSZ
; i
++) {
421 a
= (a
<< 8) | tokenbuf
[tokenptr
++]; b
+= 8;
424 *p
++ = tokmap
[(a
>> b
) & 0x3f];
428 *p
++ = tokmap
[(a
<< (6 - b
)) & 0x3f];
432 static void client_line(char *line
, size_t len
, void *p
)
434 struct client
*c
= p
;
436 struct passwd
*pw
= 0;
437 const struct policy
*pol
;
439 struct policy upol
= POLICY_INIT(A_LIMIT
);
440 struct policy_file pf
;
444 c
->q
.s
[L
].port
= c
->q
.s
[R
].port
= 0;
446 disconnect_client(c
);
450 if (fwatch_update(&polfw
, "yaid.policy")) {
451 logmsg(0, LOG_INFO
, "reload master policy file `%s'", "yaid.policy");
452 load_policy_file("yaid.policy", &policy
);
456 if (unum(&q
, &c
->q
.s
[L
].port
, 1, 65535)) goto bad
;
457 skipws(&q
); if (*q
!= ',') goto bad
; q
++;
458 if (unum(&q
, &c
->q
.s
[R
].port
, 1, 65535)) goto bad
;
459 skipws(&q
); if (*q
) goto bad
;
464 if ((pw
= getpwuid(c
->q
.u
.uid
)) == 0) {
465 logmsg(&c
->q
, LOG_ERR
, "no passwd entry for user %d", c
->q
.u
.uid
);
466 reply_error(c
, E_NOUSER
);
474 /* Should already be logged. */
475 reply_error(c
, c
->q
.u
.error
);
481 for (i
= 0; i
< DA_LEN(&policy
); i
++) {
482 pol
= &DA(&policy
)[i
];
483 if (!match_policy(pol
, &c
->q
)) continue;
484 if (pol
->act
.act
!= A_USER
)
487 dstr_putf(&d
, "%s/.yaid.policy", pw
->pw_dir
);
488 if (open_policy_file(&pf
, d
.buf
, "user policy file", &c
->q
))
490 while (!read_policy_file(&pf
)) {
492 logmsg(&c
->q
, LOG_ERR
, "%s:%d: user policy file too long",
496 if (!match_policy(&pf
.p
, &c
->q
)) continue;
497 if (!(pol
->act
.u
.user
& (1 << pf
.p
.act
.act
))) {
498 logmsg(&c
->q
, LOG_ERR
,
499 "%s:%d: user action forbidden by global policy",
503 upol
= pf
.p
; pol
= &upol
;
505 close_policy_file(&pf
);
508 close_policy_file(&pf
);
510 pol
= &default_policy
;
514 switch (pol
->act
.act
) {
516 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d)", pw
->pw_name
, c
->q
.u
.uid
);
517 reply(c
, "USERID", "UNIX", pw
->pw_name
);
521 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); token = %s",
522 pw
->pw_name
, c
->q
.u
.uid
, buf
);
523 reply(c
, "USERID", "OTHER", buf
);
526 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); denying",
527 pw
->pw_name
, c
->q
.u
.uid
);
530 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); hiding",
531 pw
->pw_name
, c
->q
.u
.uid
);
532 reply_error(c
, E_HIDDEN
);
535 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); lie = `%s'",
536 pw
->pw_name
, c
->q
.u
.uid
, pol
->act
.u
.lie
);
537 reply(c
, "USERID", "UNIX", pol
->act
.u
.lie
);
547 logmsg(&c
->q
, LOG_ERR
, "failed to parse query from client");
548 disconnect_client(c
);
551 static void accept_client(int fd
, unsigned mode
, void *p
)
553 struct listen
*l
= p
;
555 struct sockaddr_storage ssr
, ssl
;
556 size_t ssz
= sizeof(ssr
);
559 if ((sk
= accept(fd
, (struct sockaddr
*)&ssr
, &ssz
)) < 0) {
560 if (errno
!= EAGAIN
&& errno
== EWOULDBLOCK
) {
561 logmsg(0, LOG_ERR
, "failed to accept incoming %s connection: %s",
562 l
->ao
->name
, strerror(errno
));
566 if (fix_up_socket(sk
, "incoming client")) { close(sk
); return; }
568 c
= xmalloc(sizeof(*c
));
571 l
->ao
->sockaddr_to_addr(&ssr
, &c
->q
.s
[R
].addr
);
573 if (getsockname(sk
, (struct sockaddr
*)&ssl
, &ssz
)) {
575 "failed to read local address for incoming %s connection: %s",
576 l
->ao
->name
, strerror(errno
));
581 l
->ao
->sockaddr_to_addr(&ssl
, &c
->q
.s
[L
].addr
);
582 c
->q
.s
[L
].port
= c
->q
.s
[R
].port
= 0;
584 /* logmsg(&c->q, LOG_INFO, "accepted %s connection", l->ao->name); */
586 selbuf_init(&c
->b
, &sel
, sk
, client_line
, c
);
587 selbuf_setsize(&c
->b
, 1024);
590 init_writebuf(&c
->wb
, sk
, done_client_write
, c
);
593 static int make_listening_socket(const struct addrops
*ao
, int port
)
598 struct sockaddr_storage ss
;
602 if ((fd
= socket(ao
->af
, SOCK_STREAM
, 0)) < 0) {
603 if (errno
== EAFNOSUPPORT
) return (-1);
604 die(1, "failed to create %s listening socket: %s",
605 ao
->name
, strerror(errno
));
607 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(yes
));
610 ao
->socket_to_sockaddr(&s
, &ss
, &ssz
);
611 if (ao
->init_listen_socket(fd
)) {
612 die(1, "failed to initialize %s listening socket: %s",
613 ao
->name
, strerror(errno
));
615 if (bind(fd
, (struct sockaddr
*)&ss
, ssz
)) {
616 die(1, "failed to bind %s listening socket: %s",
617 ao
->name
, strerror(errno
));
619 if (fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0)) {
620 die(1, "failed to set %s listening socket nonblocking: %s",
621 ao
->name
, strerror(errno
));
624 die(1, "failed to listen for %s: %s", ao
->name
, strerror(errno
));
626 l
= xmalloc(sizeof(*l
));
628 sel_initfile(&sel
, &l
->f
, fd
, SEL_READ
, accept_client
, l
);
634 int main(int argc
, char *argv
[])
637 const struct addrops
*ao
;
642 fwatch_init(&polfw
, "yaid.policy");
644 if (load_policy_file("yaid.policy", &policy
))
647 for (i
= 0; i
< DA_LEN(&policy
); i
++)
648 print_policy(&DA(&policy
)[i
]);
651 if ((randfd
= open("/dev/urandom", O_RDONLY
)) < 0) {
652 die(1, "failed to open `/dev/urandom' for reading: %s",
657 for (ao
= addroptab
; ao
->name
; ao
++)
658 if (!make_listening_socket(ao
, port
)) any
= 1;
660 die(1, "no IP protocols supported");
663 if (sel_select(&sel
)) die(1, "select failed: %s", strerror(errno
));
668 /*----- That's all, folks -------------------------------------------------*/