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