Imported Upstream version 3.3.1
[debian/amanda] / common-src / bsd-security.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: bsd-security.c,v 1.75 2006/07/19 17:41:14 martinea Exp $
28  *
29  * "BSD" security module
30  */
31
32 #include "amanda.h"
33 #include "util.h"
34 #include "clock.h"
35 #include "dgram.h"
36 #include "event.h"
37 #include "packet.h"
38 #include "security.h"
39 #include "security-util.h"
40 #include "sockaddr-util.h"
41 #include "stream.h"
42
43 #ifndef SO_RCVBUF
44 #undef DUMPER_SOCKET_BUFFERING
45 #endif
46
47 /*
48  * Interface functions
49  */
50 static void     bsd_connect(const char *, char *(*)(char *, void *), 
51                         void (*)(void *, security_handle_t *, security_status_t),
52                         void *, void *);
53 static void     bsd_accept(const struct security_driver *,
54                         char *(*)(char *, void *),
55                         int, int,
56                         void (*)(security_handle_t *, pkt_t *),
57                         void *);
58 static void     bsd_close(void *);
59 static void *   bsd_stream_server(void *);
60 static int      bsd_stream_accept(void *);
61 static void *   bsd_stream_client(void *, int);
62 static void     bsd_stream_close(void *);
63 static int      bsd_stream_auth(void *);
64 static int      bsd_stream_id(void *);
65 static void     bsd_stream_read(void *, void (*)(void *, void *, ssize_t), void *);
66 static ssize_t  bsd_stream_read_sync(void *, void **);
67 static void     bsd_stream_read_cancel(void *);
68
69 /*
70  * This is our interface to the outside world
71  */
72 const security_driver_t bsd_security_driver = {
73     "BSD",
74     bsd_connect,
75     bsd_accept,
76     sec_get_authenticated_peer_name_hostname,
77     bsd_close,
78     udpbsd_sendpkt,
79     udp_recvpkt,
80     udp_recvpkt_cancel,
81     bsd_stream_server,
82     bsd_stream_accept,
83     bsd_stream_client,
84     bsd_stream_close,
85     bsd_stream_auth,
86     bsd_stream_id,
87     tcp_stream_write,
88     bsd_stream_read,
89     bsd_stream_read_sync,
90     bsd_stream_read_cancel,
91     sec_close_connection_none,
92     NULL,
93     NULL
94 };
95
96 /*
97  * This is data local to the datagram socket.  We have one datagram
98  * per process, so it is global.
99  */
100 static udp_handle_t netfd4;
101 static udp_handle_t netfd6;
102 static int not_init4 = 1;
103 static int not_init6 = 1;
104
105 /* generate new handles from here */
106 static int newhandle = 0;
107
108 /*
109  * These are the internal helper functions
110  */
111 static void     stream_read_callback(void *);
112 static void     stream_read_sync_callback(void *);
113
114 /*
115  * Setup and return a handle outgoing to a client
116  */
117
118 static void
119 bsd_connect(
120     const char *        hostname,
121     char *              (*conf_fn)(char *, void *),
122     void                (*fn)(void *, security_handle_t *, security_status_t),
123     void *              arg,
124     void *              datap)
125 {
126     struct sec_handle *bh;
127     in_port_t port = 0;
128     struct timeval sequence_time;
129     int sequence;
130     char *handle;
131     int result;
132     struct addrinfo *res, *res_addr;
133     char *canonname;
134     int result_bind;
135     char *service;
136
137     assert(hostname != NULL);
138
139     (void)conf_fn;      /* Quiet unused parameter warning */
140     (void)datap;        /* Quiet unused parameter warning */
141
142     bh = g_new0(struct sec_handle, 1);
143     bh->proto_handle=NULL;
144     security_handleinit(&bh->sech, &bsd_security_driver);
145
146     result = resolve_hostname(hostname, SOCK_DGRAM, &res, &canonname);
147     if(result != 0) {
148         dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
149         security_seterror(&bh->sech, _("resolve_hostname(%s): %s\n"), hostname,
150                           gai_strerror(result));
151         (*fn)(arg, &bh->sech, S_ERROR);
152         return;
153     }
154     if (canonname == NULL) {
155         dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
156         security_seterror(&bh->sech,
157                 _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
158         (*fn)(arg, &bh->sech, S_ERROR);
159         if (res) freeaddrinfo(res);
160         return;
161     }
162     if (res == NULL) {
163         dbprintf(_("resolve_hostname(%s): no results\n"), hostname);
164         security_seterror(&bh->sech,
165                 _("resolve_hostname(%s): no results\n"), hostname);
166         (*fn)(arg, &bh->sech, S_ERROR);
167         amfree(canonname);
168         return;
169     }
170
171     for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
172 #ifdef WORKING_IPV6
173         /* IPv6 socket already bound */
174         if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 0) {
175             break;
176         }
177         /*
178          * Only init the IPv6 socket once
179          */
180         if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 1) {
181             dgram_zero(&netfd6.dgram);
182
183             set_root_privs(1);
184             result_bind = dgram_bind(&netfd6.dgram,
185                                      res_addr->ai_addr->sa_family, &port);
186             set_root_privs(0);
187             if (result_bind != 0) {
188                 continue;
189             }
190             netfd6.handle = NULL;
191             netfd6.pkt.body = NULL;
192             netfd6.recv_security_ok = &bsd_recv_security_ok;
193             netfd6.prefix_packet = &bsd_prefix_packet;
194             /*
195              * We must have a reserved port.  Bomb if we didn't get one.
196              */
197             if (port >= IPPORT_RESERVED) {
198                 security_seterror(&bh->sech,
199                     _("unable to bind to a reserved port (got port %u)"),
200                     (unsigned int)port);
201                 (*fn)(arg, &bh->sech, S_ERROR);
202                 freeaddrinfo(res);
203                 amfree(canonname);
204                 return;
205             }
206             not_init6 = 0;
207             bh->udp = &netfd6;
208             break;
209         }
210 #endif
211
212         /* IPv4 socket already bound */
213         if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 0) {
214             break;
215         }
216
217         /*
218          * Only init the IPv4 socket once
219          */
220         if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 1) {
221             dgram_zero(&netfd4.dgram);
222
223             set_root_privs(1);
224             result_bind = dgram_bind(&netfd4.dgram,
225                                      res_addr->ai_addr->sa_family, &port);
226             set_root_privs(0);
227             if (result_bind != 0) {
228                 continue;
229             }
230             netfd4.handle = NULL;
231             netfd4.pkt.body = NULL;
232             netfd4.recv_security_ok = &bsd_recv_security_ok;
233             netfd4.prefix_packet = &bsd_prefix_packet;
234             /*
235              * We must have a reserved port.  Bomb if we didn't get one.
236              */
237             if (port >= IPPORT_RESERVED) {
238                 security_seterror(&bh->sech,
239                     "unable to bind to a reserved port (got port %u)",
240                     (unsigned int)port);
241                 (*fn)(arg, &bh->sech, S_ERROR);
242                 freeaddrinfo(res);
243                 amfree(canonname);
244                 return;
245             }
246             not_init4 = 0;
247             bh->udp = &netfd4;
248             break;
249         }
250     }
251
252     if (res_addr == NULL) {
253         dbprintf(_("Can't bind a socket to connect to %s\n"), hostname);
254         security_seterror(&bh->sech,
255                 _("Can't bind a socket to connect to %s\n"), hostname);
256         (*fn)(arg, &bh->sech, S_ERROR);
257        amfree(canonname);
258        return;
259     }
260
261 #ifdef WORKING_IPV6
262     if (res_addr->ai_addr->sa_family == AF_INET6)
263         bh->udp = &netfd6;
264     else
265 #endif
266         bh->udp = &netfd4;
267
268     auth_debug(1, _("Resolved hostname=%s\n"), canonname);
269
270     if (conf_fn) {
271         service = conf_fn("client_port", datap);
272         if (!service || strlen(service) <= 1)
273             service = "amanda";
274     } else {
275         service = "amanda";
276     }
277     port = find_port_for_service(service, "udp");
278     if (port == 0) {
279         security_seterror(&bh->sech, _("%s/udp unknown protocol"), service);
280         (*fn)(arg, &bh->sech, S_ERROR);
281         amfree(canonname);
282         return;
283     }
284
285     amanda_gettimeofday(&sequence_time);
286     sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec;
287     handle=alloc(15);
288     g_snprintf(handle, 14, "000-%08x",  (unsigned)newhandle++);
289     if (udp_inithandle(bh->udp, bh, canonname,
290         (sockaddr_union *)res_addr->ai_addr, port, handle, sequence) < 0) {
291         (*fn)(arg, &bh->sech, S_ERROR);
292         amfree(bh->hostname);
293         amfree(bh);
294     }
295     else {
296         (*fn)(arg, &bh->sech, S_OK);
297     }
298     amfree(handle);
299     amfree(canonname);
300
301     freeaddrinfo(res);
302 }
303
304 /*
305  * Setup to accept new incoming connections
306  */
307 static void
308 bsd_accept(
309     const struct security_driver *      driver,
310     char       *(*conf_fn)(char *, void *),
311     int         in,
312     int         out,
313     void        (*fn)(security_handle_t *, pkt_t *),
314     void       *datap)
315 {
316     struct stat sbuf;
317
318     assert(in >= 0 && out >= 0);
319     assert(fn != NULL);
320
321     (void)out;  /* Quiet unused parameter warning */
322     (void)driver; /* Quiet unused parameter warning */
323     (void)conf_fn;
324     (void)datap;
325
326     /*
327      * We assume in and out point to the same socket, and just use
328      * in.
329      */
330     dgram_socket(&netfd4.dgram, in);
331     dgram_socket(&netfd6.dgram, in);
332
333     /*
334      * Assign the function and return.  When they call recvpkt later,
335      * the recvpkt callback will call this function when it discovers
336      * new incoming connections
337      */
338     netfd4.accept_fn = fn;
339     netfd4.recv_security_ok = &bsd_recv_security_ok;
340     netfd4.prefix_packet = &bsd_prefix_packet;
341     netfd4.driver = &bsd_security_driver;
342
343     /* check if in is a socket */
344     fstat(in, &sbuf);
345     if (S_ISSOCK(sbuf.st_mode)) {
346         udp_addref(&netfd4, &udp_netfd_read_callback);
347     } else {
348         g_warning("input file descriptor is not a socket; cannot use BSD auth");
349     }
350 }
351
352 /*
353  * Frees a handle allocated by the above
354  */
355 static void
356 bsd_close(
357     void *      cookie)
358 {
359     struct sec_handle *bh = cookie;
360
361     if(bh->proto_handle == NULL) {
362         return;
363     }
364
365     auth_debug(1, _("bsd: close handle '%s'\n"), bh->proto_handle);
366
367     udp_recvpkt_cancel(bh);
368     if(bh->next) {
369         bh->next->prev = bh->prev;
370     }
371     else {
372         if (!not_init6 && netfd6.bh_last == bh)
373             netfd6.bh_last = bh->prev;
374         else
375             netfd4.bh_last = bh->prev;
376     }
377     if(bh->prev) {
378         bh->prev->next = bh->next;
379     }
380     else {
381         if (!not_init6 && netfd6.bh_first == bh)
382             netfd6.bh_first = bh->next;
383         else
384             netfd4.bh_first = bh->next;
385     }
386
387     amfree(bh->proto_handle);
388     amfree(bh->hostname);
389     amfree(bh);
390 }
391
392 /*
393  * Create the server end of a stream.  For bsd, this means setup a tcp
394  * socket for receiving a connection.
395  */
396 static void *
397 bsd_stream_server(
398     void *      h)
399 {
400     struct sec_stream *bs = NULL;
401     struct sec_handle *bh = h;
402
403     assert(bh != NULL);
404
405     bs = g_new0(struct sec_stream, 1);
406     security_streaminit(&bs->secstr, &bsd_security_driver);
407     bs->socket = stream_server(SU_GET_FAMILY(&bh->udp->peer), &bs->port,
408                                (size_t)STREAM_BUFSIZE, (size_t)STREAM_BUFSIZE,
409                                0);
410     if (bs->socket < 0) {
411         security_seterror(&bh->sech,
412             _("can't create server stream: %s"), strerror(errno));
413         amfree(bs);
414         return (NULL);
415     }
416     bs->fd = -1;
417     bs->ev_read = NULL;
418     return (bs);
419 }
420
421 /*
422  * Accepts a new connection on unconnected streams.  Assumes it is ok to
423  * block on accept()
424  */
425 static int
426 bsd_stream_accept(
427     void *      s)
428 {
429     struct sec_stream *bs = s;
430
431     assert(bs != NULL);
432     assert(bs->socket != -1);
433     assert(bs->fd < 0);
434
435     bs->fd = stream_accept(bs->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
436     if (bs->fd < 0) {
437         security_stream_seterror(&bs->secstr,
438             _("can't accept new stream connection: %s"), strerror(errno));
439         return (-1);
440     }
441     return (0);
442 }
443
444 /*
445  * Return a connected stream
446  */
447 static void *
448 bsd_stream_client(
449     void *      h,
450     int         id)
451 {
452     struct sec_stream *bs = NULL;
453     struct sec_handle *bh = h;
454 #ifdef DUMPER_SOCKET_BUFFERING
455     int rcvbuf = SIZEOF(bs->databuf) * 2;
456 #endif
457
458     assert(bh != NULL);
459
460     bs = g_new0(struct sec_stream, 1);
461     security_streaminit(&bs->secstr, &bsd_security_driver);
462     bs->fd = stream_client(bh->hostname, (in_port_t)id,
463         STREAM_BUFSIZE, STREAM_BUFSIZE, &bs->port, 0);
464     if (bs->fd < 0) {
465         security_seterror(&bh->sech,
466             _("can't connect stream to %s port %d: %s"), bh->hostname,
467             id, strerror(errno));
468         amfree(bs);
469         return (NULL);
470     }
471     bs->socket = -1;    /* we're a client */
472     bs->ev_read = NULL;
473 #ifdef DUMPER_SOCKET_BUFFERING
474     setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, SIZEOF(rcvbuf));
475 #endif
476     return (bs);
477 }
478
479 /*
480  * Close and unallocate resources for a stream
481  */
482 static void
483 bsd_stream_close(
484     void *      s)
485 {
486     struct sec_stream *bs = s;
487
488     assert(bs != NULL);
489
490     if (bs->fd != -1)
491         aclose(bs->fd);
492     if (bs->socket != -1)
493         aclose(bs->socket);
494     bsd_stream_read_cancel(bs);
495     amfree(bs);
496 }
497
498 /*
499  * Authenticate a stream.  bsd streams have no authentication
500  */
501 static int
502 bsd_stream_auth(
503     void *      s)
504 {
505     (void)s;            /* Quiet unused parameter warning */
506
507     return (0); /* success */
508 }
509
510 /*
511  * Returns the stream id for this stream.  This is just the local port.
512  */
513 static int
514 bsd_stream_id(
515     void *      s)
516 {
517     struct sec_stream *bs = s;
518
519     assert(bs != NULL);
520
521     return ((int)bs->port);
522 }
523
524 /*
525  * Submit a request to read some data.  Calls back with the given function
526  * and arg when completed.
527  */
528 static void
529 bsd_stream_read(
530     void *      s,
531     void        (*fn)(void *, void *, ssize_t),
532     void *      arg)
533 {
534     struct sec_stream *bs = s;
535
536     /*
537      * Only one read request can be active per stream.
538      */
539     if (bs->ev_read != NULL)
540         event_release(bs->ev_read);
541
542     bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD, stream_read_callback, bs);
543     bs->fn = fn;
544     bs->arg = arg;
545 }
546
547 /* buffer for bsd_stream_read_sync function */
548 static ssize_t  sync_pktlen;
549 static void    *sync_pkt;
550
551 /*
552  * Read a chunk of data to a stream.  Blocks until completion.
553  */
554 static ssize_t
555 bsd_stream_read_sync(
556     void *      s,
557     void **     buf)
558 {
559     struct sec_stream *bs = s;
560
561     assert(bs != NULL);
562
563     /*
564      * Only one read request can be active per stream.
565      */
566     if(bs->ev_read != NULL) {
567         return -1;
568     }
569     sync_pktlen = 0;
570     sync_pkt = NULL;
571     bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD,
572                         stream_read_sync_callback, bs);
573     event_wait(bs->ev_read);
574     *buf = sync_pkt;
575     return (sync_pktlen);
576 }
577
578
579 /*
580  * Callback for bsd_stream_read_sync
581  */
582 static void
583 stream_read_sync_callback(
584     void *      s)
585 {
586     struct sec_stream *bs = s;
587     ssize_t n;
588
589     assert(bs != NULL);
590
591     auth_debug(1, _("bsd: stream_read_callback_sync: fd %d\n"), bs->fd);
592
593     /*
594      * Remove the event first, in case they reschedule it in the callback.
595      */
596     bsd_stream_read_cancel(bs);
597     do {
598         n = read(bs->fd, bs->databuf, sizeof(bs->databuf));
599     } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
600     if (n < 0)
601         security_stream_seterror(&bs->secstr, "%s", strerror(errno));
602     bs->len = n;
603     sync_pktlen = bs->len;
604     sync_pkt = malloc(sync_pktlen);
605     memcpy(sync_pkt, bs->databuf, sync_pktlen);
606 }
607
608 /*
609  * Cancel a previous stream read request.  It's ok if we didn't
610  * have a read scheduled.
611  */
612 static void
613 bsd_stream_read_cancel(
614     void *      s)
615 {
616     struct sec_stream *bs = s;
617
618     assert(bs != NULL);
619     if (bs->ev_read != NULL) {
620         event_release(bs->ev_read);
621         bs->ev_read = NULL;
622     }
623 }
624
625 /*
626  * Callback for bsd_stream_read
627  */
628 static void
629 stream_read_callback(
630     void *      arg)
631 {
632     struct sec_stream *bs = arg;
633     ssize_t n;
634
635     assert(bs != NULL);
636
637     /*
638      * Remove the event first, in case they reschedule it in the callback.
639      */
640     bsd_stream_read_cancel(bs);
641     do {
642         n = read(bs->fd, bs->databuf, SIZEOF(bs->databuf));
643     } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
644
645     if (n < 0)
646         security_stream_seterror(&bs->secstr, "%s", strerror(errno));
647
648     (*bs->fn)(bs->arg, bs->databuf, n);
649 }