Imported Upstream version 3.3.1
[debian/amanda] / common-src / bsdudp-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: bsdudp-security.c,v 1.7 2006/07/05 13:18:20 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 "stream.h"
41
42 #ifndef SO_RCVBUF
43 #undef DUMPER_SOCKET_BUFFERING
44 #endif
45
46 /*
47  * Interface functions
48  */
49 static void bsdudp_connect(const char *,
50     char *(*)(char *, void *), 
51     void (*)(void *, security_handle_t *, security_status_t), void *, void *);
52 static void bsdudp_accept(const struct security_driver *,
53     char *(*)(char *, void *),
54     int, int,
55     void (*)(security_handle_t *, pkt_t *),
56     void *);
57 static void bsdudp_close(void *);
58
59 /*
60  * This is our interface to the outside world
61  */
62 const security_driver_t bsdudp_security_driver = {
63     "BSDUDP",
64     bsdudp_connect,
65     bsdudp_accept,
66     sec_get_authenticated_peer_name_hostname,
67     bsdudp_close,
68     udpbsd_sendpkt,
69     udp_recvpkt,
70     udp_recvpkt_cancel,
71     tcp1_stream_server,
72     tcp1_stream_accept,
73     tcp1_stream_client,
74     tcpma_stream_close,
75     sec_stream_auth,
76     sec_stream_id,
77     tcpm_stream_write,
78     tcpm_stream_read,
79     tcpm_stream_read_sync,
80     tcpm_stream_read_cancel,
81     sec_close_connection_none,
82     NULL,
83     NULL
84 };
85
86 /*
87  * This is data local to the datagram socket.  We have one datagram
88  * per process, so it is global.
89  */
90 static udp_handle_t netfd4;
91 static udp_handle_t netfd6;
92 static int not_init4 = 1;
93 static int not_init6 = 1;
94
95 /* generate new handles from here */
96 static unsigned int newhandle = 0;
97
98 /*
99  * Setup and return a handle outgoing to a client
100  */
101 static void
102 bsdudp_connect(
103     const char *hostname,
104     char *      (*conf_fn)(char *, void *),
105     void        (*fn)(void *, security_handle_t *, security_status_t),
106     void *      arg,
107     void *      datap)
108 {
109     struct sec_handle *bh;
110     in_port_t port;
111     struct timeval sequence_time;
112     int sequence;
113     char *handle;
114     int result;
115     char *canonname;
116     struct addrinfo *res = NULL, *res_addr;
117     int result_bind;
118     char *service;
119
120     (void)conf_fn;      /* Quiet unused parameter warning */
121     (void)datap;        /* Quiet unused parameter warning */
122     assert(hostname != NULL);
123
124     bh = g_new0(struct sec_handle, 1);
125     bh->proto_handle=NULL;
126     bh->rc = NULL;
127     security_handleinit(&bh->sech, &bsdudp_security_driver);
128
129     result = resolve_hostname(hostname, SOCK_DGRAM, &res, &canonname);
130     if(result != 0) {
131         dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
132         security_seterror(&bh->sech, _("resolve_hostname(%s): %s\n"), hostname,
133                           gai_strerror(result));
134         (*fn)(arg, &bh->sech, S_ERROR);
135         return;
136     }
137     if (canonname == NULL) {
138         dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
139         security_seterror(&bh->sech,
140                 _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
141         (*fn)(arg, &bh->sech, S_ERROR);
142        return;
143     }
144     if (res == NULL) {
145         dbprintf(_("resolve_hostname(%s): no results\n"), hostname);
146         security_seterror(&bh->sech,
147                 _("resolve_hostname(%s): no results\n"), hostname);
148         (*fn)(arg, &bh->sech, S_ERROR);
149        amfree(canonname);
150        return;
151     }
152
153     for (res_addr = res; res_addr != NULL; res_addr = res_addr->ai_next) {
154 #ifdef WORKING_IPV6
155         /* IPv6 socket already bound */
156         if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 0) {
157             break;
158         }
159         /*
160          * Only init the IPv6 socket once
161          */
162         if (res_addr->ai_addr->sa_family == AF_INET6 && not_init6 == 1) {
163             dgram_zero(&netfd6.dgram);
164
165             set_root_privs(1);
166             result_bind = dgram_bind(&netfd6.dgram,
167                                      res_addr->ai_addr->sa_family, &port);
168             set_root_privs(0);
169             if (result_bind != 0) {
170                 continue;
171             }
172             netfd6.handle = NULL;
173             netfd6.pkt.body = NULL;
174             netfd6.recv_security_ok = &bsd_recv_security_ok;
175             netfd6.prefix_packet = &bsd_prefix_packet;
176             /*
177              * We must have a reserved port.  Bomb if we didn't get one.
178              */
179             if (port >= IPPORT_RESERVED) {
180                 security_seterror(&bh->sech,
181                     _("unable to bind to a reserved port (got port %u)"),
182                     (unsigned int)port);
183                 (*fn)(arg, &bh->sech, S_ERROR);
184                 freeaddrinfo(res);
185                 amfree(canonname);
186                 return;
187             }
188             not_init6 = 0;
189             bh->udp = &netfd6;
190             break;
191         }
192 #endif
193
194         /* IPv4 socket already bound */
195         if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 0) {
196             break;
197         }
198
199         /*
200          * Only init the IPv4 socket once
201          */
202         if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 1) {
203             dgram_zero(&netfd4.dgram);
204
205             set_root_privs(1);
206             result_bind = dgram_bind(&netfd4.dgram,
207                                      res_addr->ai_addr->sa_family, &port);
208             set_root_privs(0);
209             if (result_bind != 0) {
210                 continue;
211             }
212             netfd4.handle = NULL;
213             netfd4.pkt.body = NULL;
214             netfd4.recv_security_ok = &bsd_recv_security_ok;
215             netfd4.prefix_packet = &bsd_prefix_packet;
216             /*
217              * We must have a reserved port.  Bomb if we didn't get one.
218              */
219             if (port >= IPPORT_RESERVED) {
220                 security_seterror(&bh->sech,
221                     "unable to bind to a reserved port (got port %u)",
222                     (unsigned int)port);
223                 (*fn)(arg, &bh->sech, S_ERROR);
224                 freeaddrinfo(res);
225                 amfree(canonname);
226                 return;
227             }
228             not_init4 = 0;
229             bh->udp = &netfd4;
230             break;
231         }
232     }
233
234     if (res_addr == NULL) {
235         dbprintf(_("Can't bind a socket to connect to %s\n"), hostname);
236         security_seterror(&bh->sech,
237                 _("Can't bind a socket to connect to %s\n"), hostname);
238         (*fn)(arg, &bh->sech, S_ERROR);
239        amfree(canonname);
240        return;
241     }
242
243 #ifdef WORKING_IPV6
244     if (res_addr->ai_addr->sa_family == AF_INET6)
245         bh->udp = &netfd6;
246     else
247 #endif
248         bh->udp = &netfd4;
249
250     auth_debug(1, _("Resolved hostname=%s\n"), canonname);
251     if (conf_fn) {
252         service = conf_fn("client_port", datap);
253         if (!service || strlen(service) <= 1)
254             service = "amanda";
255     } else {
256         service = "amanda";
257     }
258     port = find_port_for_service(service, "udp");
259     if (port == 0) {
260         security_seterror(&bh->sech, _("%s/udp unknown protocol"), service);
261         (*fn)(arg, &bh->sech, S_ERROR);
262         amfree(canonname);
263         return;
264     }
265
266     amanda_gettimeofday(&sequence_time);
267     sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec;
268     handle=alloc(15);
269     g_snprintf(handle,14,"000-%08x", newhandle++);
270     if (udp_inithandle(bh->udp, bh, canonname,
271                        (sockaddr_union *)res_addr->ai_addr, port,
272                        handle, sequence) < 0) {
273         (*fn)(arg, &bh->sech, S_ERROR);
274         amfree(bh->hostname);
275         amfree(bh);
276     } else {
277         (*fn)(arg, &bh->sech, S_OK);
278     }
279     amfree(handle);
280     amfree(canonname);
281
282     if (res) freeaddrinfo(res);
283 }
284
285 /*
286  * Setup to accept new incoming connections
287  */
288 static void
289 bsdudp_accept(
290     const struct security_driver *driver,
291     char *      (*conf_fn)(char *, void *),
292     int         in,
293     int         out,
294     void        (*fn)(security_handle_t *, pkt_t *),
295     void       *datap)
296 {
297     (void)driver;       /* Quiet unused parameter warning */
298     (void)out;          /* Quiet unused parameter warning */
299     (void)conf_fn;
300     (void)datap;
301
302     assert(in >= 0 && out >= 0);
303     assert(fn != NULL);
304
305     /*
306      * We assume in and out point to the same socket, and just use
307      * in.
308      */
309     dgram_socket(&netfd4.dgram, in);
310     dgram_socket(&netfd6.dgram, in);
311
312     /*
313      * Assign the function and return.  When they call recvpkt later,
314      * the recvpkt callback will call this function when it discovers
315      * new incoming connections
316      */
317     netfd4.accept_fn = fn;
318     netfd4.recv_security_ok = &bsd_recv_security_ok;
319     netfd4.prefix_packet = &bsd_prefix_packet;
320     netfd4.driver = &bsdudp_security_driver;
321
322
323     udp_addref(&netfd4, &udp_netfd_read_callback);
324 }
325
326 /*
327  * Frees a handle allocated by the above
328  */
329 static void
330 bsdudp_close(
331     void *cookie)
332 {
333     struct sec_handle *bh = cookie;
334
335     if(bh->proto_handle == NULL) {
336         return;
337     }
338
339     auth_debug(1, _("bsdudp: close handle '%s'\n"), bh->proto_handle);
340
341     udp_recvpkt_cancel(bh);
342     if(bh->next) {
343         bh->next->prev = bh->prev;
344     }
345     else {
346         if (!not_init6 && netfd6.bh_last == bh)
347             netfd6.bh_last = bh->prev;
348         else
349             netfd4.bh_last = bh->prev;
350     }
351     if(bh->prev) {
352         bh->prev->next = bh->next;
353     }
354     else {
355         if (!not_init6 && netfd6.bh_first == bh)
356             netfd6.bh_first = bh->next;
357         else
358             netfd4.bh_first = bh->next;
359     }
360
361     amfree(bh->proto_handle);
362     amfree(bh->hostname);
363     amfree(bh);
364 }
365