Imported Upstream version 2.5.2p1
[debian/amanda] / common-src / ssh-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: ssh-security.c,v 1.23 2006/08/21 20:17:10 martinea Exp $
29  *
30  * ssh-security.c - security and transport over ssh or a ssh-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 ssh 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 SSH_SECURITY
47
48 /*
49  * Number of seconds ssh has to start up
50  */
51 #define CONNECT_TIMEOUT 20
52
53 /*
54  * Magic values for ssh_conn->handle
55  */
56 #define H_TAKEN -1              /* ssh_conn->tok was already read */
57 #define H_EOF   -2              /* this connection has been shut down */
58
59 /*
60  * Interface functions
61  */
62 static void     ssh_connect(const char *, char *(*)(char *, void *),
63                         void (*)(void *, security_handle_t *, security_status_t),
64                         void *, void *);
65
66 /*
67  * This is our interface to the outside world.
68  */
69 const security_driver_t ssh_security_driver = {
70     "SSH",
71     ssh_connect,
72     sec_accept,
73     sec_close,
74     stream_sendpkt,
75     stream_recvpkt,
76     stream_recvpkt_cancel,
77     tcpma_stream_server,
78     tcpma_stream_accept,
79     tcpma_stream_client,
80     tcpma_stream_close,
81     sec_stream_auth,
82     sec_stream_id,
83     tcpm_stream_write,
84     tcpm_stream_read,
85     tcpm_stream_read_sync,
86     tcpm_stream_read_cancel,
87     tcpm_close_connection,
88     NULL,
89     NULL
90 };
91
92 static int newhandle = 1;
93
94 /*
95  * Local functions
96  */
97 static int runssh(struct tcp_conn *, const char *, const char *, const char *);
98
99 /*
100  * ssh version of a security handle allocator.  Logically sets
101  * up a network "connection".
102  */
103 static void
104 ssh_connect(
105     const char *        hostname,
106     char *              (*conf_fn)(char *, void *),
107     void                (*fn)(void *, security_handle_t *, security_status_t),
108     void *              arg,
109     void *              datap)
110 {
111     struct sec_handle *rh;
112     char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL;
113
114     assert(fn != NULL);
115     assert(hostname != NULL);
116
117     (void)conf_fn;      /* Quiet unused parameter warning */
118
119     auth_debug(1, ("%s: ssh: ssh_connect: %s\n", debug_prefix_time(NULL),
120                    hostname));
121
122     rh = alloc(SIZEOF(*rh));
123     security_handleinit(&rh->sech, &ssh_security_driver);
124     rh->hostname = NULL;
125     rh->rs = NULL;
126     rh->ev_timeout = NULL;
127     rh->rc = NULL;
128
129     rh->hostname = NULL;
130     if (try_resolving_hostname(hostname, &rh->hostname)) {
131         security_seterror(&rh->sech,
132             "%s: ssh could not resolve hostname", hostname);
133         (*fn)(arg, &rh->sech, S_ERROR);
134         return;
135     }
136     rh->rs = tcpma_stream_client(rh, newhandle++);
137
138     if (rh->rs == NULL)
139         goto error;
140
141     amfree(rh->hostname);
142     rh->hostname = stralloc(rh->rs->rc->hostname);
143
144     /*
145      * We need to open a new connection.
146      *
147      * XXX need to eventually limit number of outgoing connections here.
148      */
149     if(conf_fn) {
150         amandad_path    = conf_fn("amandad_path", datap);
151         client_username = conf_fn("client_username", datap);
152         ssh_keys        = conf_fn("ssh_keys", datap);
153     }
154     if(rh->rc->read == -1) {
155         if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys) < 0) {
156             security_seterror(&rh->sech, "can't connect to %s: %s",
157                               hostname, rh->rs->rc->errmsg);
158             goto error;
159         }
160         rh->rc->refcnt++;
161     }
162
163     /*
164      * The socket will be opened async so hosts that are down won't
165      * block everything.  We need to register a write event
166      * so we will know when the socket comes alive.
167      *
168      * Overload rh->rs->ev_read to provide a write event handle.
169      * We also register a timeout.
170      */
171     rh->fn.connect = fn;
172     rh->arg = arg;
173     rh->rs->ev_read = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
174         sec_connect_callback, rh);
175     rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
176         sec_connect_timeout, rh);
177
178     return;
179
180 error:
181     (*fn)(arg, &rh->sech, S_ERROR);
182 }
183
184 /*
185  * Forks a ssh to the host listed in rc->hostname
186  * Returns negative on error, with an errmsg in rc->errmsg.
187  */
188 static int
189 runssh(
190     struct tcp_conn *   rc,
191     const char *        amandad_path,
192     const char *        client_username,
193     const char *        ssh_keys)
194 {
195     int rpipe[2], wpipe[2];
196     char *xamandad_path = (char *)amandad_path;
197     char *xclient_username = (char *)client_username;
198     char *xssh_keys = (char *)ssh_keys;
199
200     memset(rpipe, -1, SIZEOF(rpipe));
201     memset(wpipe, -1, SIZEOF(wpipe));
202     if (pipe(rpipe) < 0 || pipe(wpipe) < 0) {
203         rc->errmsg = newvstralloc(rc->errmsg, "pipe: ", strerror(errno), NULL);
204         return (-1);
205     }
206
207     switch (rc->pid = fork()) {
208     case -1:
209         rc->errmsg = newvstralloc(rc->errmsg, "fork: ", strerror(errno), NULL);
210         aclose(rpipe[0]);
211         aclose(rpipe[1]);
212         aclose(wpipe[0]);
213         aclose(wpipe[1]);
214         return (-1);
215     case 0:
216         dup2(wpipe[0], 0);
217         dup2(rpipe[1], 1);
218         break;
219     default:
220         rc->read = rpipe[0];
221         aclose(rpipe[1]);
222         rc->write = wpipe[1];
223         aclose(wpipe[0]);
224         return (0);
225     }
226
227     safe_fd(-1, 0);
228
229     if(!xamandad_path || strlen(xamandad_path) <= 1) 
230         xamandad_path = vstralloc(libexecdir, "/", "amandad",
231                                  versionsuffix(), NULL);
232     if(!xclient_username || strlen(xclient_username) <= 1)
233         xclient_username = CLIENT_LOGIN;
234     if(!ssh_keys || strlen(ssh_keys) <= 1) {
235         execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username,
236                rc->hostname, xamandad_path, "-auth=ssh", "amdump", "amindexd",
237                "amidxtaped", (char *)NULL);
238     }
239     else {
240         execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username,
241                "-i", xssh_keys, rc->hostname, xamandad_path, "-auth=ssh",
242                "amdump", "amindexd", "amidxtaped", (char *)NULL);
243     }
244     error("error: couldn't exec %s: %s", SSH, strerror(errno));
245
246     /* should never go here, shut up compiler warning */
247     return(-1);
248 }
249
250 #endif  /* SSH_SECURITY */