Imported Upstream version 3.2.0
[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             uid_t euid;
164             dgram_zero(&netfd6.dgram);
165
166             euid = geteuid();
167             set_root_privs(1);
168             result_bind = dgram_bind(&netfd6.dgram,
169                                      res_addr->ai_addr->sa_family, &port);
170             set_root_privs(0);
171             if (result_bind != 0) {
172                 continue;
173             }
174             netfd6.handle = NULL;
175             netfd6.pkt.body = NULL;
176             netfd6.recv_security_ok = &bsd_recv_security_ok;
177             netfd6.prefix_packet = &bsd_prefix_packet;
178             /*
179              * We must have a reserved port.  Bomb if we didn't get one.
180              */
181             if (port >= IPPORT_RESERVED) {
182                 security_seterror(&bh->sech,
183                     _("unable to bind to a reserved port (got port %u)"),
184                     (unsigned int)port);
185                 (*fn)(arg, &bh->sech, S_ERROR);
186                 freeaddrinfo(res);
187                 amfree(canonname);
188                 return;
189             }
190             not_init6 = 0;
191             bh->udp = &netfd6;
192             break;
193         }
194 #endif
195
196         /* IPv4 socket already bound */
197         if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 0) {
198             break;
199         }
200
201         /*
202          * Only init the IPv4 socket once
203          */
204         if (res_addr->ai_addr->sa_family == AF_INET && not_init4 == 1) {
205             uid_t euid;
206             dgram_zero(&netfd4.dgram);
207
208             euid = geteuid();
209             set_root_privs(1);
210             result_bind = dgram_bind(&netfd4.dgram,
211                                      res_addr->ai_addr->sa_family, &port);
212             set_root_privs(0);
213             if (result_bind != 0) {
214                 continue;
215             }
216             netfd4.handle = NULL;
217             netfd4.pkt.body = NULL;
218             netfd4.recv_security_ok = &bsd_recv_security_ok;
219             netfd4.prefix_packet = &bsd_prefix_packet;
220             /*
221              * We must have a reserved port.  Bomb if we didn't get one.
222              */
223             if (port >= IPPORT_RESERVED) {
224                 security_seterror(&bh->sech,
225                     "unable to bind to a reserved port (got port %u)",
226                     (unsigned int)port);
227                 (*fn)(arg, &bh->sech, S_ERROR);
228                 freeaddrinfo(res);
229                 amfree(canonname);
230                 return;
231             }
232             not_init4 = 0;
233             bh->udp = &netfd4;
234             break;
235         }
236     }
237
238     if (res_addr == NULL) {
239         dbprintf(_("Can't bind a socket to connect to %s\n"), hostname);
240         security_seterror(&bh->sech,
241                 _("Can't bind a socket to connect to %s\n"), hostname);
242         (*fn)(arg, &bh->sech, S_ERROR);
243        amfree(canonname);
244        return;
245     }
246
247 #ifdef WORKING_IPV6
248     if (res_addr->ai_addr->sa_family == AF_INET6)
249         bh->udp = &netfd6;
250     else
251 #endif
252         bh->udp = &netfd4;
253
254     auth_debug(1, _("Resolved hostname=%s\n"), canonname);
255     if (conf_fn) {
256         service = conf_fn("client_port", datap);
257         if (!service || strlen(service) <= 1)
258             service = "amanda";
259     } else {
260         service = "amanda";
261     }
262     port = find_port_for_service(service, "udp");
263     if (port == 0) {
264         security_seterror(&bh->sech, _("%s/udp unknown protocol"), service);
265         (*fn)(arg, &bh->sech, S_ERROR);
266         amfree(canonname);
267         return;
268     }
269
270     amanda_gettimeofday(&sequence_time);
271     sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec;
272     handle=alloc(15);
273     g_snprintf(handle,14,"000-%08x", newhandle++);
274     if (udp_inithandle(bh->udp, bh, canonname,
275                        (sockaddr_union *)res_addr->ai_addr, port,
276                        handle, sequence) < 0) {
277         (*fn)(arg, &bh->sech, S_ERROR);
278         amfree(bh->hostname);
279         amfree(bh);
280     } else {
281         (*fn)(arg, &bh->sech, S_OK);
282     }
283     amfree(handle);
284     amfree(canonname);
285
286     if (res) freeaddrinfo(res);
287 }
288
289 /*
290  * Setup to accept new incoming connections
291  */
292 static void
293 bsdudp_accept(
294     const struct security_driver *driver,
295     char *      (*conf_fn)(char *, void *),
296     int         in,
297     int         out,
298     void        (*fn)(security_handle_t *, pkt_t *),
299     void       *datap)
300 {
301     (void)driver;       /* Quiet unused parameter warning */
302     (void)out;          /* Quiet unused parameter warning */
303     (void)conf_fn;
304     (void)datap;
305
306     assert(in >= 0 && out >= 0);
307     assert(fn != NULL);
308
309     /*
310      * We assume in and out point to the same socket, and just use
311      * in.
312      */
313     dgram_socket(&netfd4.dgram, in);
314     dgram_socket(&netfd6.dgram, in);
315
316     /*
317      * Assign the function and return.  When they call recvpkt later,
318      * the recvpkt callback will call this function when it discovers
319      * new incoming connections
320      */
321     netfd4.accept_fn = fn;
322     netfd4.recv_security_ok = &bsd_recv_security_ok;
323     netfd4.prefix_packet = &bsd_prefix_packet;
324     netfd4.driver = &bsdudp_security_driver;
325
326
327     udp_addref(&netfd4, &udp_netfd_read_callback);
328 }
329
330 /*
331  * Frees a handle allocated by the above
332  */
333 static void
334 bsdudp_close(
335     void *cookie)
336 {
337     struct sec_handle *bh = cookie;
338
339     if(bh->proto_handle == NULL) {
340         return;
341     }
342
343     auth_debug(1, _("bsdudp: close handle '%s'\n"), bh->proto_handle);
344
345     udp_recvpkt_cancel(bh);
346     if(bh->next) {
347         bh->next->prev = bh->prev;
348     }
349     else {
350         if (!not_init6 && netfd6.bh_last == bh)
351             netfd6.bh_last = bh->prev;
352         else
353             netfd4.bh_last = bh->prev;
354     }
355     if(bh->prev) {
356         bh->prev->next = bh->next;
357     }
358     else {
359         if (!not_init6 && netfd6.bh_first == bh)
360             netfd6.bh_first = bh->next;
361         else
362             netfd4.bh_first = bh->next;
363     }
364
365     amfree(bh->proto_handle);
366     amfree(bh->hostname);
367     amfree(bh);
368 }
369