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