Imported Upstream version 3.2.0
[debian/amanda] / common-src / bsdtcp-security.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1999 University of Maryland
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 /*
28  * $Id: bsdtcp-security.c,v 1.7 2006/07/13 03:22:20 paddy_s Exp $
29  *
30  * bsdtcp-security.c - security and transport over bsdtcp or a bsdtcp-like command.
31  *
32  * XXX still need to check for initial keyword on connect so we can skip
33  * over shell garbage and other stuff that bsdtcp might want to spew out.
34  */
35
36 #include "amanda.h"
37 #include "util.h"
38 #include "event.h"
39 #include "packet.h"
40 #include "security.h"
41 #include "security-util.h"
42 #include "sockaddr-util.h"
43 #include "stream.h"
44
45 /*
46  * Number of seconds bsdtcp has to start up
47  */
48 #define CONNECT_TIMEOUT 20
49
50 /*
51  * Interface functions
52  */
53 static void bsdtcp_accept(const struct security_driver *,
54     char *(*)(char *, void *),
55     int, int,
56     void (*)(security_handle_t *, pkt_t *),
57     void *);
58 static void bsdtcp_connect(const char *,
59     char *(*)(char *, void *), 
60     void (*)(void *, security_handle_t *, security_status_t), void *, void *);
61
62 /*
63  * This is our interface to the outside world.
64  */
65 const security_driver_t bsdtcp_security_driver = {
66     "BSDTCP",
67     bsdtcp_connect,
68     bsdtcp_accept,
69     sec_get_authenticated_peer_name_hostname,
70     sec_close,
71     stream_sendpkt,
72     stream_recvpkt,
73     stream_recvpkt_cancel,
74     tcpma_stream_server,
75     tcpma_stream_accept,
76     tcpma_stream_client,
77     tcpma_stream_close,
78     sec_stream_auth,
79     sec_stream_id,
80     tcpm_stream_write,
81     tcpm_stream_read,
82     tcpm_stream_read_sync,
83     tcpm_stream_read_cancel,
84     tcpm_close_connection,
85     NULL,
86     NULL
87 };
88
89 static int newhandle = 1;
90
91 /*
92  * Local functions
93  */
94 static int runbsdtcp(struct sec_handle *, in_port_t port);
95
96
97 /*
98  * bsdtcp version of a security handle allocator.  Logically sets
99  * up a network "connection".
100  */
101 static void
102 bsdtcp_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 *rh;
110     int result;
111     char *canonname;
112     char *service;
113     in_port_t port;
114
115     assert(fn != NULL);
116     assert(hostname != NULL);
117     (void)conf_fn;      /* Quiet unused parameter warning */
118     (void)datap;        /* Quiet unused parameter warning */
119
120     auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname);
121
122     rh = g_new0(struct sec_handle, 1);
123     security_handleinit(&rh->sech, &bsdtcp_security_driver);
124     rh->hostname = NULL;
125     rh->rs = NULL;
126     rh->ev_timeout = NULL;
127     rh->rc = NULL;
128
129     result = resolve_hostname(hostname, 0, NULL, &canonname);
130     if(result != 0) {
131         dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
132         security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
133                           gai_strerror(result));
134         (*fn)(arg, &rh->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(&rh->sech,
140                 _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
141         (*fn)(arg, &rh->sech, S_ERROR);
142        return;
143     }
144
145     rh->hostname = canonname;   /* will be replaced */
146     canonname = NULL; /* steal reference */
147     rh->rs = tcpma_stream_client(rh, newhandle++);
148     rh->rc->recv_security_ok = &bsd_recv_security_ok;
149     rh->rc->prefix_packet = &bsd_prefix_packet;
150
151     if (rh->rs == NULL)
152         goto error;
153
154     amfree(rh->hostname);
155     rh->hostname = stralloc(rh->rs->rc->hostname);
156
157     if (conf_fn) {
158         service = conf_fn("client_port", datap);
159         if (!service || strlen(service) <= 1)
160             service = "amanda";
161     } else {
162         service = "amanda";
163     }
164     port = find_port_for_service(service, "tcp");
165     if (port == 0) {
166         security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
167         goto error;
168     }
169
170     /*
171      * We need to open a new connection.
172      *
173      * XXX need to eventually limit number of outgoing connections here.
174      */
175     if(rh->rc->read == -1) {
176         if (runbsdtcp(rh, port) < 0)
177             goto error;
178         rh->rc->refcnt++;
179     }
180
181     /*
182      * The socket will be opened async so hosts that are down won't
183      * block everything.  We need to register a write event
184      * so we will know when the socket comes alive.
185      *
186      * Overload rh->rs->ev_read to provide a write event handle.
187      * We also register a timeout.
188      */
189     rh->fn.connect = fn;
190     rh->arg = arg;
191     rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
192         EV_WRITEFD, sec_connect_callback, rh);
193     rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
194         sec_connect_timeout, rh);
195
196     return;
197
198 error:
199     (*fn)(arg, &rh->sech, S_ERROR);
200 }
201
202 /*
203  * Setup to handle new incoming connections
204  */
205 static void
206 bsdtcp_accept(
207     const struct security_driver *driver,
208     char *      (*conf_fn)(char *, void *),
209     int         in,
210     int         out,
211     void        (*fn)(security_handle_t *, pkt_t *),
212     void       *datap)
213 {
214     sockaddr_union sin;
215     socklen_t_equiv len;
216     struct tcp_conn *rc;
217     char hostname[NI_MAXHOST];
218     int result;
219     char *errmsg = NULL;
220
221     len = sizeof(sin);
222     if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) {
223         dbprintf(_("getpeername returned: %s\n"), strerror(errno));
224         return;
225     }
226     if ((result = getnameinfo((struct sockaddr *)&sin, len,
227                               hostname, NI_MAXHOST, NULL, 0, 0) != 0)) {
228         dbprintf(_("getnameinfo failed: %s\n"),
229                   gai_strerror(result));
230         return;
231     }
232     if (check_name_give_sockaddr(hostname,
233                                  (struct sockaddr *)&sin, &errmsg) < 0) {
234         amfree(errmsg);
235         return;
236     }
237
238     rc = sec_tcp_conn_get(hostname, 0);
239     rc->recv_security_ok = &bsd_recv_security_ok;
240     rc->prefix_packet = &bsd_prefix_packet;
241     copy_sockaddr(&rc->peer, &sin);
242     rc->read = in;
243     rc->write = out;
244     rc->accept_fn = fn;
245     rc->driver = driver;
246     rc->conf_fn = conf_fn;
247     rc->datap = datap;
248     sec_tcp_conn_read(rc);
249 }
250
251 /*
252  * Forks a bsdtcp to the host listed in rc->hostname
253  * Returns negative on error, with an errmsg in rc->errmsg.
254  */
255 static int
256 runbsdtcp(
257     struct sec_handle * rh,
258     in_port_t port)
259 {
260     int                 server_socket;
261     in_port_t           my_port;
262     struct tcp_conn *   rc = rh->rc;
263
264     set_root_privs(1);
265
266     server_socket = stream_client_privileged(rc->hostname,
267                                      port,
268                                      STREAM_BUFSIZE,
269                                      STREAM_BUFSIZE,
270                                      &my_port,
271                                      0);
272     set_root_privs(0);
273
274     if(server_socket < 0) {
275         security_seterror(&rh->sech,
276             "%s", strerror(errno));
277         
278         return -1;
279     }
280
281     if(my_port >= IPPORT_RESERVED) {
282         security_seterror(&rh->sech,
283                           _("did not get a reserved port: %d"), my_port);
284     }
285
286     rc->read = rc->write = server_socket;
287     return 0;
288 }