2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1999 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: bsd-security.c,v 1.75.2.1 2006/09/28 18:46:08 martinea Exp $
29 * "BSD" security module
39 #include "security-util.h"
46 #define bsdprintf(x) dbprintf(x)
52 #undef DUMPER_SOCKET_BUFFERING
55 #ifdef BSD_SECURITY /* { */
58 * Change the following from #undef to #define to cause detailed logging
59 * of the security steps, e.g. into /tmp/amanda/amandad*debug.
61 #undef SHOW_SECURITY_DETAIL
63 #if defined(TEST) /* { */
64 #define SHOW_SECURITY_DETAIL
66 #define bsdprintf(p) printf p
72 static void bsd_connect(const char *, char *(*)(char *, void *),
73 void (*)(void *, security_handle_t *, security_status_t),
75 static void bsd_accept(const struct security_driver *, int, int,
76 void (*)(security_handle_t *, pkt_t *));
77 static void bsd_close(void *);
78 static void * bsd_stream_server(void *);
79 static int bsd_stream_accept(void *);
80 static void * bsd_stream_client(void *, int);
81 static void bsd_stream_close(void *);
82 static int bsd_stream_auth(void *);
83 static int bsd_stream_id(void *);
84 static void bsd_stream_read(void *, void (*)(void *, void *, ssize_t), void *);
85 static ssize_t bsd_stream_read_sync(void *, void **);
86 static void bsd_stream_read_cancel(void *);
89 * This is our interface to the outside world
91 const security_driver_t bsd_security_driver = {
107 bsd_stream_read_sync,
108 bsd_stream_read_cancel,
109 sec_close_connection_none,
113 * This is data local to the datagram socket. We have one datagram
114 * per process, so it is global.
116 static udp_handle_t netfd;
117 static int not_init = 1;
119 /* generate new handles from here */
120 static int newhandle = 0;
123 * These are the internal helper functions
125 static void stream_read_callback(void *);
126 static void stream_read_sync_callback(void *);
129 * Setup and return a handle outgoing to a client
134 const char * hostname,
135 char * (*conf_fn)(char *, void *),
136 void (*fn)(void *, security_handle_t *, security_status_t),
140 struct sec_handle *bh;
144 struct timeval sequence_time;
145 amanda_timezone dontcare;
149 assert(hostname != NULL);
151 (void)conf_fn; /* Quiet unused parameter warning */
152 (void)datap; /* Quiet unused parameter warning */
154 bh = alloc(SIZEOF(*bh));
155 bh->proto_handle=NULL;
157 security_handleinit(&bh->sech, &bsd_security_driver);
160 * Only init the socket once
164 dgram_zero(&netfd.dgram);
168 dgram_bind(&netfd.dgram, &port);
171 netfd.pkt.body = NULL;
172 netfd.recv_security_ok = &bsd_recv_security_ok;
173 netfd.prefix_packet = &bsd_prefix_packet;
175 * We must have a reserved port. Bomb if we didn't get one.
177 if (port >= IPPORT_RESERVED) {
178 security_seterror(&bh->sech,
179 "unable to bind to a reserved port (got port %u)",
181 (*fn)(arg, &bh->sech, S_ERROR);
187 if ((he = gethostbyname(hostname)) == NULL) {
188 security_seterror(&bh->sech,
189 "%s: could not resolve hostname", hostname);
190 (*fn)(arg, &bh->sech, S_ERROR);
193 bsdprintf(("Resolved hostname=%s\n", hostname));
194 if ((se = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL)
195 port = (in_port_t)htons(AMANDA_SERVICE_DEFAULT);
197 port = (in_port_t)se->s_port;
198 amanda_gettimeofday(&sequence_time, &dontcare);
199 sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec;
201 snprintf(handle, 14, "000-%08x", (unsigned)newhandle++);
202 if (udp_inithandle(&netfd, bh, he, port, handle, sequence) < 0) {
203 (*fn)(arg, &bh->sech, S_ERROR);
204 amfree(bh->hostname);
208 (*fn)(arg, &bh->sech, S_OK);
213 * Setup to accept new incoming connections
217 const struct security_driver * driver,
220 void (*fn)(security_handle_t *, pkt_t *))
223 assert(in >= 0 && out >= 0);
226 (void)out; /* Quiet unused parameter warning */
227 (void)driver; /* Quiet unused parameter warning */
230 * We assume in and out point to the same socket, and just use
233 dgram_socket(&netfd.dgram, in);
236 * Assign the function and return. When they call recvpkt later,
237 * the recvpkt callback will call this function when it discovers
238 * new incoming connections
240 netfd.accept_fn = fn;
241 netfd.recv_security_ok = &bsd_recv_security_ok;
242 netfd.prefix_packet = &bsd_prefix_packet;
243 netfd.driver = &bsd_security_driver;
245 udp_addref(&netfd, &udp_netfd_read_callback);
249 * Frees a handle allocated by the above
255 struct sec_handle *bh = cookie;
257 if(bh->proto_handle == NULL) {
261 bsdprintf(("%s: bsd: close handle '%s'\n",
262 debug_prefix_time(NULL), bh->proto_handle));
264 udp_recvpkt_cancel(bh);
266 bh->next->prev = bh->prev;
269 netfd.bh_last = bh->prev;
272 bh->prev->next = bh->next;
275 netfd.bh_first = bh->next;
278 amfree(bh->proto_handle);
279 amfree(bh->hostname);
284 * Create the server end of a stream. For bsd, this means setup a tcp
285 * socket for receiving a connection.
292 struct sec_stream *bs = NULL;
293 struct sec_handle *bh = h;
297 bs = alloc(SIZEOF(*bs));
298 security_streaminit(&bs->secstr, &bsd_security_driver);
299 bs->socket = stream_server(&bs->port, (size_t)STREAM_BUFSIZE,
300 (size_t)STREAM_BUFSIZE, 0);
301 if (bs->socket < 0) {
302 security_seterror(&bh->sech,
303 "can't create server stream: %s", strerror(errno));
312 #endif /* !TEST */ /* } */
316 * Accepts a new connection on unconnected streams. Assumes it is ok to
324 struct sec_stream *bs = s;
327 assert(bs->socket != -1);
330 bs->fd = stream_accept(bs->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
332 security_stream_seterror(&bs->secstr,
333 "can't accept new stream connection: %s", strerror(errno));
336 #endif /* !TEST */ /* } */
341 * Return a connected stream
348 struct sec_stream *bs = NULL;
350 struct sec_handle *bh = h;
351 #ifdef DUMPER_SOCKET_BUFFERING
352 size_t rcvbuf = SIZEOF(bs->databuf) * 2;
357 bs = alloc(SIZEOF(*bs));
358 security_streaminit(&bs->secstr, &bsd_security_driver);
359 bs->fd = stream_client(bh->hostname, (in_port_t)id,
360 STREAM_BUFSIZE, STREAM_BUFSIZE, &bs->port, 0);
362 security_seterror(&bh->sech,
363 "can't connect stream to %s port %d: %s", bh->hostname,
364 id, strerror(errno));
368 bs->socket = -1; /* we're a client */
370 #ifdef DUMPER_SOCKET_BUFFERING
371 setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, SIZEOF(rcvbuf));
373 #endif /* !TEST */ /* } */
378 * Close and unallocate resources for a stream
384 struct sec_stream *bs = s;
390 if (bs->socket != -1)
392 bsd_stream_read_cancel(bs);
397 * Authenticate a stream. bsd streams have no authentication
403 (void)s; /* Quiet unused parameter warning */
405 return (0); /* success */
409 * Returns the stream id for this stream. This is just the local port.
415 struct sec_stream *bs = s;
419 return ((int)bs->port);
423 * Submit a request to read some data. Calls back with the given function
424 * and arg when completed.
429 void (*fn)(void *, void *, ssize_t),
432 struct sec_stream *bs = s;
435 * Only one read request can be active per stream.
437 if (bs->ev_read != NULL)
438 event_release(bs->ev_read);
440 bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD, stream_read_callback, bs);
446 * Read a chunk of data to a stream. Blocks until completion.
449 bsd_stream_read_sync(
453 struct sec_stream *bs = s;
458 * Only one read request can be active per stream.
460 if(bs->ev_read != NULL) {
463 bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD,
464 stream_read_sync_callback, bs);
465 event_wait(bs->ev_read);
472 * Callback for bsd_stream_read_sync
475 stream_read_sync_callback(
478 struct sec_stream *bs = s;
483 bsdprintf(("%s: bsd: stream_read_callback_sync: fd %d\n",
484 debug_prefix_time(NULL), bs->fd));
487 * Remove the event first, in case they reschedule it in the callback.
489 bsd_stream_read_cancel(bs);
491 n = read(bs->fd, bs->databuf, sizeof(bs->databuf));
492 } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
494 security_stream_seterror(&bs->secstr, strerror(errno));
499 * Cancel a previous stream read request. It's ok if we didn't
500 * have a read scheduled.
503 bsd_stream_read_cancel(
506 struct sec_stream *bs = s;
509 if (bs->ev_read != NULL) {
510 event_release(bs->ev_read);
516 * Callback for bsd_stream_read
519 stream_read_callback(
522 struct sec_stream *bs = arg;
528 * Remove the event first, in case they reschedule it in the callback.
530 bsd_stream_read_cancel(bs);
532 n = read(bs->fd, bs->databuf, SIZEOF(bs->databuf));
533 } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
536 security_stream_seterror(&bs->secstr, strerror(errno));
538 (*bs->fn)(bs->arg, bs->databuf, n);
541 #endif /* BSD_SECURITY */ /* } */
543 #if defined(TEST) /* { */
546 * The following dummy bind_portrange function is so we do not need to
547 * drag in util.o just for the test program.
552 struct sockaddr_in *addrp,
553 in_port_t first_port,
557 (void)s; /* Quiet unused parameter warning */
558 (void)addrp; /* Quiet unused parameter warning */
559 (void)first_port; /* Quiet unused parameter warning */
560 (void)last_port; /* Quiet unused parameter warning */
561 (void)proto; /* Quiet unused parameter warning */
567 * Construct a datestamp (YYYYMMDD) from a time_t.
574 char datestamp[3*NUM_STR_SIZE];
578 when = time((time_t *)NULL);
582 tm = localtime(&when);
583 snprintf(datestamp, SIZEOF(datestamp),
584 "%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
585 return stralloc(datestamp);
589 * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t.
596 char timestamp[6*NUM_STR_SIZE];
600 when = time((time_t *)NULL);
604 tm = localtime(&when);
605 snprintf(timestamp, SIZEOF(timestamp),
606 "%04d%02d%02d%02d%02d%02d",
607 tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
608 tm->tm_hour, tm->tm_min, tm->tm_sec);
609 return stralloc(timestamp);
613 * The following are so we can include security.o but not all the rest
614 * of the security modules.
616 const security_driver_t krb4_security_driver = {};
617 const security_driver_t krb5_security_driver = {};
618 const security_driver_t rsh_security_driver = {};
619 const security_driver_t ssh_security_driver = {};
620 const security_driver_t bsdtcp_security_driver = {};
621 const security_driver_t bsdudp_security_driver = {};
624 * This function will be called to accept the connection and is used
625 * to report success or failure.
627 static void fake_accept_function(
628 security_handle_t * handle,
632 fputs(handle->error, stdout);
635 fputs("access is allowed\n", stdout);
647 struct sec_handle *bh;
649 struct passwd *pwent;
651 /* Don't die when child closes pipe */
652 signal(SIGPIPE, SIG_IGN);
655 * The following is stolen from amandad to emulate what it would
658 if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
659 client_uid = pwent->pw_uid;
660 client_gid = pwent->pw_gid;
665 /* we'd rather not run as root */
666 if (geteuid() == 0) {
667 if(client_uid == (uid_t) -1) {
668 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
671 initgroups(CLIENT_LOGIN, client_gid);
676 #endif /* FORCE_USERID */
679 fputs("Remote user: ", stdout);
684 remoteuser = agets(stdin);
685 if (remoteuser == NULL)
687 } while (remoteuser[0] == '\0');
690 fputs("Remote host: ", stdout);
696 remotehost = agets(stdin);
697 if (remotehost == NULL)
699 } while (remotehost[0] == '\0');
701 set_pname("security");
706 if ((hp = gethostbyname(remotehost)) == NULL) {
707 fprintf(stderr, "cannot look up remote host %s\n", remotehost);
710 memcpy((char *)&netfd.peer.sin_addr,
714 * Fake that it is coming from a reserved port.
716 netfd.peer.sin_port = htons(IPPORT_RESERVED - 1);
718 bh = alloc(SIZEOF(*bh));
719 bh->proto_handle=NULL;
721 netfd.pkt.type = P_REQ;
722 dgram_zero(&netfd.dgram);
723 save_cur = netfd.dgram.cur; /* cheating */
724 dgram_cat(&netfd.dgram, "%s", pkthdr2str(bh, &netfd.pkt));
725 dgram_cat(&netfd.dgram, "SECURITY USER %s\n", remoteuser);
726 netfd.dgram.cur = save_cur; /* cheating */
728 netfd.accept_fn = fake_accept_function;
729 netfd.recv_security_ok = &bsd_recv_security_ok;
730 netfd.prefix_packet = &bsd_prefix_packet;
731 udp_netfd_read_callback(&netfd);