50985d97d3b132eb50abbf30c1ddc68a3aabef61
[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 "queue.h"
41 #include "security.h"
42 #include "security-util.h"
43 #include "sockaddr-util.h"
44 #include "stream.h"
45 #include "version.h"
46
47 /*
48  * Number of seconds bsdtcp has to start up
49  */
50 #define CONNECT_TIMEOUT 20
51
52 /*
53  * Interface functions
54  */
55 static void bsdtcp_accept(const struct security_driver *,
56     char *(*)(char *, void *),
57     int, int,
58     void (*)(security_handle_t *, pkt_t *),
59     void *);
60 static void bsdtcp_connect(const char *,
61     char *(*)(char *, void *), 
62     void (*)(void *, security_handle_t *, security_status_t), void *, void *);
63
64 /*
65  * This is our interface to the outside world.
66  */
67 const security_driver_t bsdtcp_security_driver = {
68     "BSDTCP",
69     bsdtcp_connect,
70     bsdtcp_accept,
71     sec_close,
72     stream_sendpkt,
73     stream_recvpkt,
74     stream_recvpkt_cancel,
75     tcpma_stream_server,
76     tcpma_stream_accept,
77     tcpma_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     tcpm_close_connection,
86     NULL,
87     NULL
88 };
89
90 static int newhandle = 1;
91
92 /*
93  * Local functions
94  */
95 static int runbsdtcp(struct sec_handle *);
96
97
98 /*
99  * bsdtcp version of a security handle allocator.  Logically sets
100  * up a network "connection".
101  */
102 static void
103 bsdtcp_connect(
104     const char *hostname,
105     char *      (*conf_fn)(char *, void *),
106     void        (*fn)(void *, security_handle_t *, security_status_t),
107     void *      arg,
108     void *      datap)
109 {
110     struct sec_handle *rh;
111     int result;
112     char *canonname;
113
114     assert(fn != NULL);
115     assert(hostname != NULL);
116     (void)conf_fn;      /* Quiet unused parameter warning */
117     (void)datap;        /* Quiet unused parameter warning */
118
119     auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname);
120
121     rh = alloc(sizeof(*rh));
122     security_handleinit(&rh->sech, &bsdtcp_security_driver);
123     rh->hostname = NULL;
124     rh->rs = NULL;
125     rh->ev_timeout = NULL;
126     rh->rc = NULL;
127
128     result = resolve_hostname(hostname, 0, NULL, &canonname);
129     if(result != 0) {
130         dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
131         security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
132                           gai_strerror(result));
133         (*fn)(arg, &rh->sech, S_ERROR);
134         return;
135     }
136     if (canonname == NULL) {
137         dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
138         security_seterror(&rh->sech,
139                 _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
140         (*fn)(arg, &rh->sech, S_ERROR);
141        return;
142     }
143
144     rh->hostname = canonname;   /* will be replaced */
145     canonname = NULL; /* steal reference */
146     rh->rs = tcpma_stream_client(rh, newhandle++);
147     rh->rc->recv_security_ok = &bsd_recv_security_ok;
148     rh->rc->prefix_packet = &bsd_prefix_packet;
149
150     if (rh->rs == NULL)
151         goto error;
152
153     amfree(rh->hostname);
154     rh->hostname = stralloc(rh->rs->rc->hostname);
155
156     /*
157      * We need to open a new connection.
158      *
159      * XXX need to eventually limit number of outgoing connections here.
160      */
161     if(rh->rc->read == -1) {
162         if (runbsdtcp(rh) < 0)
163             goto error;
164         rh->rc->refcnt++;
165     }
166
167     /*
168      * The socket will be opened async so hosts that are down won't
169      * block everything.  We need to register a write event
170      * so we will know when the socket comes alive.
171      *
172      * Overload rh->rs->ev_read to provide a write event handle.
173      * We also register a timeout.
174      */
175     rh->fn.connect = fn;
176     rh->arg = arg;
177     rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
178         EV_WRITEFD, sec_connect_callback, rh);
179     rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
180         sec_connect_timeout, rh);
181
182     return;
183
184 error:
185     (*fn)(arg, &rh->sech, S_ERROR);
186 }
187
188 /*
189  * Setup to handle new incoming connections
190  */
191 static void
192 bsdtcp_accept(
193     const struct security_driver *driver,
194     char *      (*conf_fn)(char *, void *),
195     int         in,
196     int         out,
197     void        (*fn)(security_handle_t *, pkt_t *),
198     void       *datap)
199 {
200     sockaddr_union sin;
201     socklen_t_equiv len;
202     struct tcp_conn *rc;
203     char hostname[NI_MAXHOST];
204     int result;
205     char *errmsg = NULL;
206
207     len = sizeof(sin);
208     if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) {
209         dbprintf(_("getpeername returned: %s\n"), strerror(errno));
210         return;
211     }
212     if ((result = getnameinfo((struct sockaddr *)&sin, len,
213                               hostname, NI_MAXHOST, NULL, 0, 0) != 0)) {
214         dbprintf(_("getnameinfo failed: %s\n"),
215                   gai_strerror(result));
216         return;
217     }
218     if (check_name_give_sockaddr(hostname,
219                                  (struct sockaddr *)&sin, &errmsg) < 0) {
220         amfree(errmsg);
221         return;
222     }
223
224     rc = sec_tcp_conn_get(hostname, 0);
225     rc->recv_security_ok = &bsd_recv_security_ok;
226     rc->prefix_packet = &bsd_prefix_packet;
227     copy_sockaddr(&rc->peer, &sin);
228     rc->read = in;
229     rc->write = out;
230     rc->accept_fn = fn;
231     rc->driver = driver;
232     rc->conf_fn = conf_fn;
233     rc->datap = datap;
234     sec_tcp_conn_read(rc);
235 }
236
237 /*
238  * Forks a bsdtcp to the host listed in rc->hostname
239  * Returns negative on error, with an errmsg in rc->errmsg.
240  */
241 static int
242 runbsdtcp(
243     struct sec_handle * rh)
244 {
245     struct servent *    sp;
246     int                 server_socket;
247     in_port_t           my_port;
248     struct tcp_conn *   rc = rh->rc;
249
250     if ((sp = getservbyname(AMANDA_SERVICE_NAME, "tcp")) == NULL) {
251         error(_("%s/tcp unknown protocol"), "amanda");
252     }
253
254     set_root_privs(1);
255
256     server_socket = stream_client_privileged(rc->hostname,
257                                      (in_port_t)(ntohs((in_port_t)sp->s_port)),
258                                      STREAM_BUFSIZE,
259                                      STREAM_BUFSIZE,
260                                      &my_port,
261                                      0);
262     set_root_privs(0);
263
264     if(server_socket < 0) {
265         security_seterror(&rh->sech,
266             "%s", strerror(errno));
267         
268         return -1;
269     }
270
271     if(my_port >= IPPORT_RESERVED) {
272         security_seterror(&rh->sech,
273                           _("did not get a reserved port: %d"), my_port);
274     }
275
276     rc->read = rc->write = server_socket;
277     return 0;
278 }