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_get_authenticated_peer_name_gethostname,
65     sec_close,
66     stream_sendpkt,
67     stream_recvpkt,
68     stream_recvpkt_cancel,
69     tcpma_stream_server,
70     tcpma_stream_accept,
71     tcpma_stream_client,
72     tcpma_stream_close,
73     sec_stream_auth,
74     sec_stream_id,
75     tcpm_stream_write,
76     tcpm_stream_read,
77     tcpm_stream_read_sync,
78     tcpm_stream_read_cancel,
79     tcpm_close_connection,
80     NULL,
81     NULL
82 };
83 
84 static int newhandle = 1;
85 
86 /*
87  * Local functions
88  */
89 static int runlocal(struct tcp_conn *, const char *, const char *);
90 
91 
92 /*
93  * local version of a security handle allocator.  Logically sets
94  * up a network "connection".
95  */
96 static void
local_connect(const char * hostname,char * (* conf_fn)(char *,void *),void (* fn)(void *,security_handle_t *,security_status_t),void * arg,void * datap)97 local_connect(
98     const char *	hostname,
99     char *		(*conf_fn)(char *, void *),
100     void		(*fn)(void *, security_handle_t *, security_status_t),
101     void *		arg,
102     void *		datap)
103 {
104     struct sec_handle *rh;
105     char *amandad_path=NULL;
106     char *client_username=NULL;
107     char myhostname[MAX_HOSTNAME_LENGTH+1];
108 
109     assert(fn != NULL);
110     assert(hostname != NULL);
111 
112     auth_debug(1, _("local: local_connect: %s\n"), hostname);
113 
114     rh = g_new0(struct sec_handle, 1);
115     security_handleinit(&rh->sech, &local_security_driver);
116     rh->hostname = NULL;
117     rh->rs = NULL;
118     rh->ev_timeout = NULL;
119     rh->rc = NULL;
120 
121     if (gethostname(myhostname, MAX_HOSTNAME_LENGTH) == -1) {
122 	security_seterror(&rh->sech, _("gethostname failed"));
123 	(*fn)(arg, &rh->sech, S_ERROR);
124 	return;
125     }
126     myhostname[SIZEOF(myhostname)-1] = '\0';
127 
128     if (strcmp(hostname, myhostname) != 0 &&
129 	match("^localhost(\\.localdomain)?$", hostname) == 0) {
130 	security_seterror(&rh->sech,
131 	    _("%s: is not local"), hostname);
132 	(*fn)(arg, &rh->sech, S_ERROR);
133 	return;
134     }
135     rh->hostname = stralloc(hostname);
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     }
153     if(rh->rc->read == -1) {
154 	if (runlocal(rh->rs->rc, amandad_path, client_username) < 0) {
155 	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
156 			      hostname, rh->rs->rc->errmsg);
157 	    goto error;
158 	}
159 	rh->rc->refcnt++;
160     }
161 
162     /*
163      * The socket will be opened async so hosts that are down won't
164      * block everything.  We need to register a write event
165      * so we will know when the socket comes alive.
166      *
167      * Overload rh->rs->ev_read to provide a write event handle.
168      * We also register a timeout.
169      */
170     rh->fn.connect = fn;
171     rh->arg = arg;
172     rh->rs->ev_read = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
173 	sec_connect_callback, rh);
174     rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
175 	sec_connect_timeout, rh);
176 
177     return;
178 
179 error:
180     (*fn)(arg, &rh->sech, S_ERROR);
181     amfree(rh->hostname);
182 }
183 
184 /*
185  * Forks a local to the host listed in rc->hostname
186  * Returns negative on error, with an errmsg in rc->errmsg.
187  */
188 static int
runlocal(struct tcp_conn * rc,const char * amandad_path,const char * client_username G_GNUC_UNUSED)189 runlocal(
190     struct tcp_conn *	rc,
191     const char *	amandad_path,
192     const char *	client_username G_GNUC_UNUSED)
193 {
194     int rpipe[2], wpipe[2];
195     char *xamandad_path = (char *)amandad_path;
196 
197 #ifndef SINGLE_USERID
198     struct passwd *pwd = NULL;
199     uid_t uid = 0;
200     gid_t gid = 0;
201 
202     if (getuid() == 0) {
203 	if (client_username && strlen(client_username) > 1) {
204 	    pwd = getpwnam(client_username);
205             if (!pwd) {
206 		dbprintf("User '%s' doesn't exist\n", client_username);
207 	    } else {
208 		uid = pwd->pw_uid;
209 		gid = pwd->pw_gid;
210 	    }
211 	}
212 	if (!pwd) {
213 	    uid = get_client_uid();
214 	    gid = get_client_gid();
215 	}
216     }
217 #endif
218 
219     memset(rpipe, -1, SIZEOF(rpipe));
220     memset(wpipe, -1, SIZEOF(wpipe));
221     if (pipe(rpipe) < 0 || pipe(wpipe) < 0) {
222 	rc->errmsg = newvstrallocf(rc->errmsg, _("pipe: %s"), strerror(errno));
223 	return (-1);
224     }
225 
226     switch (rc->pid = fork()) {
227     case -1:
228 	rc->errmsg = newvstrallocf(rc->errmsg, _("fork: %s"), strerror(errno));
229 	aclose(rpipe[0]);
230 	aclose(rpipe[1]);
231 	aclose(wpipe[0]);
232 	aclose(wpipe[1]);
233 	return (-1);
234     case 0:
235 	dup2(wpipe[0], 0);
236 	dup2(rpipe[1], 1);
237 	break;
238     default:
239 	rc->read = rpipe[0];
240 	aclose(rpipe[1]);
241 	rc->write = wpipe[1];
242 	aclose(wpipe[0]);
243 	return (0);
244     }
245 
246     /* drop root privs for good */
247     set_root_privs(-1);
248 
249     if(!xamandad_path || strlen(xamandad_path) <= 1)
250 	xamandad_path = vstralloc(amlibexecdir, "/", "amandad", NULL);
251 
252 #ifndef SINGLE_USERID
253     if (client_username && *client_username != '\0') {
254 	initgroups(client_username, gid);
255     } else {
256 	initgroups(CLIENT_LOGIN, gid);
257     }
258     if (gid != 0)
259 	setregid(uid, gid);
260     if (uid != 0)
261 	setreuid(uid, uid);
262 #endif
263 
264     safe_fd(-1, 0);
265 
266     execlp(xamandad_path, xamandad_path,
267 	   "-auth=local", (char *)NULL);
268     error(_("error: couldn't exec %s: %s"), xamandad_path, strerror(errno));
269 
270     /* should never go here, shut up compiler warning */
271     return(-1);
272 }
273