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