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 void done_client_write(int err
, void *p
)
170 struct client
*c
= p
;
173 selbuf_enable(&c
->b
);
175 logmsg(&c
->q
, LOG_ERR
, "failed to send reply: %s", strerror(err
));
176 disconnect_client(c
);
180 static void write_to_client(struct client
*c
, const char *fmt
, ...)
187 n
= vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
189 logmsg(&c
->q
, LOG_ERR
, "failed to format output: %s", strerror(errno
));
190 disconnect_client(c
);
192 } else if (n
> sizeof(buf
)) {
193 logmsg(&c
->q
, LOG_ERR
, "output too long for client send buffer");
194 disconnect_client(c
);
198 selbuf_disable(&c
->b
);
199 if (queue_write(&c
->wb
, buf
, n
)) {
200 logmsg(&c
->q
, LOG_ERR
, "write buffer overflow");
201 disconnect_client(c
);
205 static void reply(struct client
*c
, const char *ty
, const char *msg
)
207 write_to_client(c
, "%u,%u:%s:%s\r\n",
208 c
->q
.s
[L
].port
, c
->q
.s
[R
].port
, ty
, msg
);
211 const char *const errtok
[] = {
212 #define DEFTOK(err, tok) tok,
217 static void reply_error(struct client
*c
, unsigned err
)
219 assert(err
< E_LIMIT
);
220 reply(c
, "ERROR", errtok
[err
]);
223 static void skipws(const char **pp
)
224 { while (isspace((unsigned char )**pp
)) (*pp
)++; }
226 static int idtoken(const char **pp
, char *q
, size_t n
)
233 if (*p
== ':' || *p
<= 32 || *p
>= 127) break;
243 static int unum(const char **pp
, unsigned *ii
, unsigned min
, unsigned max
)
250 if (!isdigit((unsigned char)**pp
)) return (-1);
251 e
= errno
; errno
= 0;
252 i
= strtoul(*pp
, &q
, 10);
253 if (errno
) return (-1);
256 if (i
< min
|| i
> max
) return (-1);
261 static void proxy_line(char *line
, size_t sz
, void *p
)
263 struct proxy
*px
= p
;
265 const char *q
= line
;
268 while (sz
&& isspace((unsigned char)line
[sz
- 1])) sz
--;
269 printf("received proxy line from %s: %s\n", px
->nat
, line
);
271 if (unum(&q
, &lp
, 1, 65535)) goto syntax
;
272 skipws(&q
); if (*q
!= ',') goto syntax
; q
++;
273 if (unum(&q
, &rp
, 1, 65535)) goto syntax
;
274 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
275 if (lp
!= px
->c
->q
.u
.nat
.port
|| rp
!= px
->c
->q
.s
[R
].port
) goto syntax
;
276 if (idtoken(&q
, buf
, sizeof(buf
))) goto syntax
;
277 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
278 if (strcmp(buf
, "ERROR") == 0) {
280 logmsg(&px
->c
->q
, LOG_ERR
, "proxy error from %s: %s", px
->nat
, q
);
281 reply(px
->c
, "ERROR", q
);
282 } else if (strcmp(buf
, "USERID") == 0) {
283 if (idtoken(&q
, buf
, sizeof(buf
))) goto syntax
;
284 skipws(&q
); if (*q
!= ':') goto syntax
; q
++;
286 logmsg(&px
->c
->q
, LOG_ERR
, "user `%s'; proxy = %s, os = %s",
288 write_to_client(px
->c
, "%u,%u:USERID:%s:%s\r\n",
289 px
->c
->q
.s
[L
].port
, px
->c
->q
.s
[R
].port
, buf
, q
);
295 logmsg(&px
->c
->q
, LOG_ERR
, "failed to parse response from %s", px
->nat
);
296 reply_error(px
->c
, E_UNKNOWN
);
301 static void done_proxy_write(int err
, void *p
)
303 struct proxy
*px
= p
;
306 logmsg(&px
->c
->q
, LOG_ERR
, "failed to proxy query to %s: %s",
307 px
->nat
, strerror(errno
));
308 reply_error(px
->c
, E_UNKNOWN
);
312 selbuf_enable(&px
->b
);
315 static void proxy_connected(int fd
, void *p
)
317 struct proxy
*px
= p
;
322 logmsg(&px
->c
->q
, LOG_ERR
,
323 "failed to make %s proxy connection to %s: %s",
324 px
->c
->l
->ao
->name
, px
->nat
, strerror(errno
));
325 reply_error(px
->c
, E_UNKNOWN
);
331 selbuf_init(&px
->b
, &sel
, fd
, proxy_line
, px
);
332 selbuf_setsize(&px
->b
, 1024);
333 selbuf_disable(&px
->b
);
334 init_writebuf(&px
->wb
, fd
, done_proxy_write
, px
);
336 n
= sprintf(buf
, "%u,%u\r\n", px
->c
->q
.u
.nat
.port
, px
->c
->q
.s
[R
].port
);
337 queue_write(&px
->wb
, buf
, n
);
340 static void proxy_query(struct client
*c
)
343 struct sockaddr_storage ss
;
348 px
= xmalloc(sizeof(*px
));
349 inet_ntop(c
->q
.ao
->af
, &c
->q
.u
.nat
.addr
, px
->nat
, sizeof(px
->nat
));
351 if ((fd
= socket(c
->q
.ao
->af
, SOCK_STREAM
, 0)) < 0) {
352 logmsg(&c
->q
, LOG_ERR
, "failed to make %s socket for proxy: %s",
353 c
->l
->ao
->name
, strerror(errno
));
357 if (fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0)) {
358 logmsg(&c
->q
, LOG_ERR
, "failed to set %s proxy socket nonblocking: %s",
359 c
->l
->ao
->name
, strerror(errno
));
365 c
->l
->ao
->socket_to_sockaddr(&s
, &ss
, &ssz
);
366 selbuf_disable(&c
->b
);
367 if (conn_init(&px
->cn
, &sel
, fd
, (struct sockaddr
*)&ss
, ssz
,
368 proxy_connected
, px
)) {
369 logmsg(&c
->q
, LOG_ERR
, "failed to make %s proxy connection to %s: %s",
370 c
->l
->ao
->name
, px
->nat
, strerror(errno
));
374 c
->px
= px
; px
->c
= c
;
379 selbuf_enable(&c
->b
);
384 reply_error(c
, E_UNKNOWN
);
387 static const struct policy default_policy
= POLICY_INIT(A_NAME
);
389 static void user_token(char *p
)
391 static const char tokmap
[64] =
392 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-";
398 if (tokenptr
+ TOKENSZ
>= sizeof(tokenbuf
)) {
399 if (read(randfd
, tokenbuf
, sizeof(tokenbuf
)) < sizeof(tokenbuf
))
400 die(1, "unexpected short read or error from `/dev/urandom'");
404 for (i
= 0; i
< TOKENSZ
; i
++) {
405 a
= (a
<< 8) | tokenbuf
[tokenptr
++]; b
+= 8;
408 *p
++ = tokmap
[(a
>> b
) & 0x3f];
412 *p
++ = tokmap
[(a
<< (6 - b
)) & 0x3f];
416 static void client_line(char *line
, size_t len
, void *p
)
418 struct client
*c
= p
;
420 struct passwd
*pw
= 0;
421 const struct policy
*pol
;
423 struct policy upol
= POLICY_INIT(A_LIMIT
);
424 struct policy_file pf
;
428 c
->q
.s
[L
].port
= c
->q
.s
[R
].port
= 0;
430 disconnect_client(c
);
434 if (fwatch_update(&polfw
, "yaid.policy")) {
435 logmsg(0, LOG_INFO
, "reload master policy file `%s'", "yaid.policy");
436 load_policy_file("yaid.policy", &policy
);
440 if (unum(&q
, &c
->q
.s
[L
].port
, 1, 65535)) goto bad
;
441 skipws(&q
); if (*q
!= ',') goto bad
; q
++;
442 if (unum(&q
, &c
->q
.s
[R
].port
, 1, 65535)) goto bad
;
443 skipws(&q
); if (*q
) goto bad
;
448 if ((pw
= getpwuid(c
->q
.u
.uid
)) == 0) {
449 logmsg(&c
->q
, LOG_ERR
, "no passwd entry for user %d", c
->q
.u
.uid
);
450 reply_error(c
, E_NOUSER
);
458 /* Should already be logged. */
459 reply_error(c
, c
->q
.u
.error
);
465 for (i
= 0; i
< DA_LEN(&policy
); i
++) {
466 pol
= &DA(&policy
)[i
];
467 if (!match_policy(pol
, &c
->q
)) continue;
468 if (pol
->act
.act
!= A_USER
)
471 dstr_putf(&d
, "%s/.yaid.policy", pw
->pw_dir
);
472 if (open_policy_file(&pf
, d
.buf
, "user policy file", &c
->q
))
474 while (!read_policy_file(&pf
)) {
476 logmsg(&c
->q
, LOG_ERR
, "%s:%d: user policy file too long",
480 if (!match_policy(&pf
.p
, &c
->q
)) continue;
481 if (!(pol
->act
.u
.user
& (1 << pf
.p
.act
.act
))) {
482 logmsg(&c
->q
, LOG_ERR
,
483 "%s:%d: user action forbidden by global policy",
487 upol
= pf
.p
; pol
= &upol
;
489 close_policy_file(&pf
);
492 close_policy_file(&pf
);
494 pol
= &default_policy
;
498 switch (pol
->act
.act
) {
500 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d)", pw
->pw_name
, c
->q
.u
.uid
);
501 reply(c
, "USERID:UNIX", pw
->pw_name
);
505 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); token = %s",
506 pw
->pw_name
, c
->q
.u
.uid
, buf
);
507 reply(c
, "USERID:OTHER", buf
);
510 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); denying",
511 pw
->pw_name
, c
->q
.u
.uid
);
514 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); hiding",
515 pw
->pw_name
, c
->q
.u
.uid
);
516 reply_error(c
, E_HIDDEN
);
519 logmsg(&c
->q
, LOG_INFO
, "user `%s' (%d); lie = `%s'",
520 pw
->pw_name
, c
->q
.u
.uid
, pol
->act
.u
.lie
);
521 reply(c
, "USERID:UNIX", pol
->act
.u
.lie
);
531 logmsg(&c
->q
, LOG_ERR
, "failed to parse query from client");
532 disconnect_client(c
);
535 static void accept_client(int fd
, unsigned mode
, void *p
)
537 struct listen
*l
= p
;
539 struct sockaddr_storage ssr
, ssl
;
540 size_t ssz
= sizeof(ssr
);
543 if ((sk
= accept(fd
, (struct sockaddr
*)&ssr
, &ssz
)) < 0) {
544 if (errno
!= EAGAIN
&& errno
== EWOULDBLOCK
) {
545 logmsg(0, LOG_ERR
, "failed to accept incoming %s connection: %s",
546 l
->ao
->name
, strerror(errno
));
551 c
= xmalloc(sizeof(*c
));
554 l
->ao
->sockaddr_to_addr(&ssr
, &c
->q
.s
[R
].addr
);
556 if (getsockname(sk
, (struct sockaddr
*)&ssl
, &ssz
)) {
558 "failed to read local address for incoming %s connection: %s",
559 l
->ao
->name
, strerror(errno
));
564 l
->ao
->sockaddr_to_addr(&ssl
, &c
->q
.s
[L
].addr
);
565 c
->q
.s
[L
].port
= c
->q
.s
[R
].port
= 0;
567 /* logmsg(&c->q, LOG_INFO, "accepted %s connection", l->ao->name); */
569 selbuf_init(&c
->b
, &sel
, sk
, client_line
, c
);
570 selbuf_setsize(&c
->b
, 1024);
573 init_writebuf(&c
->wb
, sk
, done_client_write
, c
);
576 static int make_listening_socket(const struct addrops
*ao
, int port
)
581 struct sockaddr_storage ss
;
585 if ((fd
= socket(ao
->af
, SOCK_STREAM
, 0)) < 0) {
586 if (errno
== EAFNOSUPPORT
) return (-1);
587 die(1, "failed to create %s listening socket: %s",
588 ao
->name
, strerror(errno
));
590 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(yes
));
593 ao
->socket_to_sockaddr(&s
, &ss
, &ssz
);
594 if (ao
->init_listen_socket(fd
)) {
595 die(1, "failed to initialize %s listening socket: %s",
596 ao
->name
, strerror(errno
));
598 if (bind(fd
, (struct sockaddr
*)&ss
, ssz
)) {
599 die(1, "failed to bind %s listening socket: %s",
600 ao
->name
, strerror(errno
));
602 if (fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, 0, 0)) {
603 die(1, "failed to set %s listening socket nonblocking: %s",
604 ao
->name
, strerror(errno
));
607 die(1, "failed to listen for %s: %s", ao
->name
, strerror(errno
));
609 l
= xmalloc(sizeof(*l
));
611 sel_initfile(&sel
, &l
->f
, fd
, SEL_READ
, accept_client
, l
);
617 int main(int argc
, char *argv
[])
620 const struct addrops
*ao
;
625 fwatch_init(&polfw
, "yaid.policy");
626 if (load_policy_file("yaid.policy", &policy
))
629 for (i
= 0; i
< DA_LEN(&policy
); i
++)
630 print_policy(&DA(&policy
)[i
]);
633 if ((randfd
= open("/dev/urandom", O_RDONLY
)) < 0) {
634 die(1, "failed to open `/dev/urandom' for reading: %s",
639 for (ao
= addroptab
; ao
->name
; ao
++)
640 if (!make_listening_socket(ao
, port
)) any
= 1;
642 die(1, "no IP protocols supported");
645 if (sel_select(&sel
)) die(1, "select failed: %s", strerror(errno
));
650 /*----- That's all, folks -------------------------------------------------*/