Imported Upstream version 3.1.0
[debian/amanda] / common-src / local-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: local-security.c 6512 2007-05-24 17:00:24Z ian $
29  *
30  * local-security.c - security and transport over local or a local-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 local might want to spew out.
34  */
35
36 #include "amanda.h"
37 #include "match.h"
38 #include "util.h"
39 #include "event.h"
40 #include "packet.h"
41 #include "security.h"
42 #include "security-util.h"
43 #include "stream.h"
44
45 /*
46  * Number of seconds amandad has to start up
47  */
48 #define CONNECT_TIMEOUT 20
49
50 /*
51  * Interface functions
52  */
53 static void local_connect(const char *, char *(*)(char *, void *),
54                         void (*)(void *, security_handle_t *, security_status_t),
55                         void *, void *);
56
57 /*
58  * This is our interface to the outside world.
59  */
60 const security_driver_t local_security_driver = {
61     "LOCAL",
62     local_connect,
63     sec_accept,
64     sec_close,
65     stream_sendpkt,
66     stream_recvpkt,
67     stream_recvpkt_cancel,
68     tcpma_stream_server,
69     tcpma_stream_accept,
70     tcpma_stream_client,
71     tcpma_stream_close,
72     sec_stream_auth,
73     sec_stream_id,
74     tcpm_stream_write,
75     tcpm_stream_read,
76     tcpm_stream_read_sync,
77     tcpm_stream_read_cancel,
78     tcpm_close_connection,
79     NULL,
80     NULL
81 };
82
83 static int newhandle = 1;
84
85 /*
86  * Local functions
87  */
88 static int runlocal(struct tcp_conn *, const char *, const char *);
89
90
91 /*
92  * local version of a security handle allocator.  Logically sets
93  * up a network "connection".
94  */
95 static void
96 local_connect(
97     const char *        hostname,
98     char *              (*conf_fn)(char *, void *),
99     void                (*fn)(void *, security_handle_t *, security_status_t),
100     void *              arg,
101     void *              datap)
102 {
103     struct sec_handle *rh;
104     char *amandad_path=NULL;
105     char *client_username=NULL;
106     char myhostname[MAX_HOSTNAME_LENGTH+1];
107
108     assert(fn != NULL);
109     assert(hostname != NULL);
110
111     auth_debug(1, _("local: local_connect: %s\n"), hostname);
112
113     rh = alloc(SIZEOF(*rh));
114     security_handleinit(&rh->sech, &local_security_driver);
115     rh->hostname = NULL;
116     rh->rs = NULL;
117     rh->ev_timeout = NULL;
118     rh->rc = NULL;
119
120     if (gethostname(myhostname, MAX_HOSTNAME_LENGTH) == -1) {
121         security_seterror(&rh->sech, _("gethostname failed"));
122         (*fn)(arg, &rh->sech, S_ERROR);
123         return;
124     }
125     myhostname[SIZEOF(myhostname)-1] = '\0';
126
127     if (strcmp(hostname, myhostname) != 0 &&
128         match("^localhost(\\.localdomain)?$", hostname) == 0) {
129         security_seterror(&rh->sech,
130             _("%s: is not local"), hostname);
131         (*fn)(arg, &rh->sech, S_ERROR);
132         return;
133     }
134     rh->hostname = stralloc(hostname);
135     rh->rs = tcpma_stream_client(rh, newhandle++);
136
137     if (rh->rs == NULL)
138         goto error;
139
140     amfree(rh->hostname);
141     rh->hostname = stralloc(rh->rs->rc->hostname);
142
143     /*
144      * We need to open a new connection.
145      *
146      * XXX need to eventually limit number of outgoing connections here.
147      */
148     if(conf_fn) {
149         amandad_path    = conf_fn("amandad_path", datap);
150         client_username = conf_fn("client_username", datap);
151     }
152     if(rh->rc->read == -1) {
153         if (runlocal(rh->rs->rc, amandad_path, client_username) < 0) {
154             security_seterror(&rh->sech, _("can't connect to %s: %s"),
155                               hostname, rh->rs->rc->errmsg);
156             goto error;
157         }
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, EV_WRITEFD,
172         sec_connect_callback, rh);
173     rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
174         sec_connect_timeout, rh);
175
176     return;
177
178 error:
179     (*fn)(arg, &rh->sech, S_ERROR);
180     amfree(rh->hostname);
181 }
182
183 /*
184  * Forks a local to the host listed in rc->hostname
185  * Returns negative on error, with an errmsg in rc->errmsg.
186  */
187 static int
188 runlocal(
189     struct tcp_conn *   rc,
190     const char *        amandad_path,
191     const char *        client_username G_GNUC_UNUSED)
192 {
193     int rpipe[2], wpipe[2];
194     char *xamandad_path = (char *)amandad_path;
195
196 #ifndef SINGLE_USERID
197     struct passwd *pwd = NULL;
198     uid_t uid = 0;
199     gid_t gid = 0;
200
201     if (getuid() == 0) {
202         if (client_username && strlen(client_username) > 1) {
203             pwd = getpwnam(client_username);
204             if (!pwd) {
205                 dbprintf("User '%s' doesn't exist\n", client_username);
206             } else {
207                 uid = pwd->pw_uid;
208                 gid = pwd->pw_gid;
209             }
210         }
211         if (!pwd) {
212             uid = get_client_uid();
213             gid = get_client_gid();
214         }
215     }
216 #endif
217
218     memset(rpipe, -1, SIZEOF(rpipe));
219     memset(wpipe, -1, SIZEOF(wpipe));
220     if (pipe(rpipe) < 0 || pipe(wpipe) < 0) {
221         rc->errmsg = newvstrallocf(rc->errmsg, _("pipe: %s"), strerror(errno));
222         return (-1);
223     }
224
225     switch (rc->pid = fork()) {
226     case -1:
227         rc->errmsg = newvstrallocf(rc->errmsg, _("fork: %s"), strerror(errno));
228         aclose(rpipe[0]);
229         aclose(rpipe[1]);
230         aclose(wpipe[0]);
231         aclose(wpipe[1]);
232         return (-1);
233     case 0:
234         dup2(wpipe[0], 0);
235         dup2(rpipe[1], 1);
236         break;
237     default:
238         rc->read = rpipe[0];
239         aclose(rpipe[1]);
240         rc->write = wpipe[1];
241         aclose(wpipe[0]);
242         return (0);
243     }
244
245     /* drop root privs for good */
246     set_root_privs(-1);
247
248     safe_fd(-1, 0);
249
250     if(!xamandad_path || strlen(xamandad_path) <= 1) 
251         xamandad_path = vstralloc(amlibexecdir, "/", "amandad", NULL);
252
253 #ifndef SINGLE_USERID
254     if (uid != 0)
255         setreuid(uid, uid);
256     if (gid != 0)
257         setregid(gid, gid);
258 #endif
259
260     execlp(xamandad_path, xamandad_path,
261            "-auth=local", "amdump", "amindexd", "amidxtaped", (char *)NULL);
262     error(_("error: couldn't exec %s: %s"), xamandad_path, strerror(errno));
263
264     /* should never go here, shut up compiler warning */
265     return(-1);
266 }