Imported Upstream version 2.5.1p3
[debian/amanda] / common-src / bsd-security.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
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  * $Id: bsd-security.c,v 1.75.2.1 2006/09/28 18:46:08 martinea Exp $
28  *
29  * "BSD" security module
30  */
31
32 #include "amanda.h"
33 #include "util.h"
34 #include "clock.h"
35 #include "dgram.h"
36 #include "event.h"
37 #include "packet.h"
38 #include "security.h"
39 #include "security-util.h"
40 #include "stream.h"
41 #include "version.h"
42
43 /*#define       BSD_DEBUG*/
44
45 #ifdef BSD_DEBUG
46 #define bsdprintf(x)    dbprintf(x)
47 #else
48 #define bsdprintf(x)
49 #endif
50
51 #ifndef SO_RCVBUF
52 #undef DUMPER_SOCKET_BUFFERING
53 #endif
54
55 #ifdef BSD_SECURITY                                             /* { */
56
57 /*
58  * Change the following from #undef to #define to cause detailed logging
59  * of the security steps, e.g. into /tmp/amanda/amandad*debug.
60  */
61 #undef SHOW_SECURITY_DETAIL
62
63 #if defined(TEST)                                               /* { */
64 #define SHOW_SECURITY_DETAIL
65 #undef bsdprintf
66 #define bsdprintf(p)    printf p
67 #endif                                                          /* } */
68
69 /*
70  * Interface functions
71  */
72 static void     bsd_connect(const char *, char *(*)(char *, void *), 
73                         void (*)(void *, security_handle_t *, security_status_t),
74                         void *, void *);
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 *);
87
88 /*
89  * This is our interface to the outside world
90  */
91 const security_driver_t bsd_security_driver = {
92     "BSD",
93     bsd_connect,
94     bsd_accept,
95     bsd_close,
96     udpbsd_sendpkt,
97     udp_recvpkt,
98     udp_recvpkt_cancel,
99     bsd_stream_server,
100     bsd_stream_accept,
101     bsd_stream_client,
102     bsd_stream_close,
103     bsd_stream_auth,
104     bsd_stream_id,
105     tcp_stream_write,
106     bsd_stream_read,
107     bsd_stream_read_sync,
108     bsd_stream_read_cancel,
109     sec_close_connection_none,
110 };
111
112 /*
113  * This is data local to the datagram socket.  We have one datagram
114  * per process, so it is global.
115  */
116 static udp_handle_t netfd;
117 static int not_init = 1;
118
119 /* generate new handles from here */
120 static int newhandle = 0;
121
122 /*
123  * These are the internal helper functions
124  */
125 static void     stream_read_callback(void *);
126 static void     stream_read_sync_callback(void *);
127
128 /*
129  * Setup and return a handle outgoing to a client
130  */
131
132 static void
133 bsd_connect(
134     const char *        hostname,
135     char *              (*conf_fn)(char *, void *),
136     void                (*fn)(void *, security_handle_t *, security_status_t),
137     void *              arg,
138     void *              datap)
139 {
140     struct sec_handle *bh;
141     struct servent *se;
142     struct hostent *he;
143     in_port_t port = 0;
144     struct timeval sequence_time;
145     amanda_timezone dontcare;
146     int sequence;
147     char *handle;
148
149     assert(hostname != NULL);
150
151     (void)conf_fn;      /* Quiet unused parameter warning */
152     (void)datap;        /* Quiet unused parameter warning */
153
154     bh = alloc(SIZEOF(*bh));
155     bh->proto_handle=NULL;
156     bh->udp = &netfd;
157     security_handleinit(&bh->sech, &bsd_security_driver);
158
159     /*
160      * Only init the socket once
161      */
162     if (not_init == 1) {
163         uid_t euid;
164         dgram_zero(&netfd.dgram);
165
166         euid = geteuid();
167         seteuid((uid_t)0);
168         dgram_bind(&netfd.dgram, &port);
169         seteuid(euid);
170         netfd.handle = NULL;
171         netfd.pkt.body = NULL;
172         netfd.recv_security_ok = &bsd_recv_security_ok;
173         netfd.prefix_packet = &bsd_prefix_packet;
174         /*
175          * We must have a reserved port.  Bomb if we didn't get one.
176          */
177         if (port >= IPPORT_RESERVED) {
178             security_seterror(&bh->sech,
179                 "unable to bind to a reserved port (got port %u)",
180                 (unsigned int)port);
181             (*fn)(arg, &bh->sech, S_ERROR);
182             return;
183         }
184         not_init = 0;
185     }
186
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);
191         return;
192     }
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);
196     else
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;
200     handle=alloc(15);
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);
205         amfree(bh);
206     }
207     else
208         (*fn)(arg, &bh->sech, S_OK);
209     amfree(handle);
210 }
211
212 /*
213  * Setup to accept new incoming connections
214  */
215 static void
216 bsd_accept(
217     const struct security_driver *      driver,
218     int         in,
219     int         out,
220     void        (*fn)(security_handle_t *, pkt_t *))
221 {
222
223     assert(in >= 0 && out >= 0);
224     assert(fn != NULL);
225
226     (void)out;  /* Quiet unused parameter warning */
227     (void)driver; /* Quiet unused parameter warning */
228
229     /*
230      * We assume in and out point to the same socket, and just use
231      * in.
232      */
233     dgram_socket(&netfd.dgram, in);
234
235     /*
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
239      */
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;
244
245     udp_addref(&netfd, &udp_netfd_read_callback);
246 }
247
248 /*
249  * Frees a handle allocated by the above
250  */
251 static void
252 bsd_close(
253     void *      cookie)
254 {
255     struct sec_handle *bh = cookie;
256
257     if(bh->proto_handle == NULL) {
258         return;
259     }
260
261     bsdprintf(("%s: bsd: close handle '%s'\n",
262                debug_prefix_time(NULL), bh->proto_handle));
263
264     udp_recvpkt_cancel(bh);
265     if(bh->next) {
266         bh->next->prev = bh->prev;
267     }
268     else {
269         netfd.bh_last = bh->prev;
270     }
271     if(bh->prev) {
272         bh->prev->next = bh->next;
273     }
274     else {
275         netfd.bh_first = bh->next;
276     }
277
278     amfree(bh->proto_handle);
279     amfree(bh->hostname);
280     amfree(bh);
281 }
282
283 /*
284  * Create the server end of a stream.  For bsd, this means setup a tcp
285  * socket for receiving a connection.
286  */
287 static void *
288 bsd_stream_server(
289     void *      h)
290 {
291 #ifndef TEST                                                    /* { */
292     struct sec_stream *bs = NULL;
293     struct sec_handle *bh = h;
294
295     assert(bh != NULL);
296
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));
304         amfree(bs);
305         return (NULL);
306     }
307     bs->fd = -1;
308     bs->ev_read = NULL;
309     return (bs);
310 #else
311     return (NULL);
312 #endif /* !TEST */                                              /* } */
313 }
314
315 /*
316  * Accepts a new connection on unconnected streams.  Assumes it is ok to
317  * block on accept()
318  */
319 static int
320 bsd_stream_accept(
321     void *      s)
322 {
323 #ifndef TEST                                                    /* { */
324     struct sec_stream *bs = s;
325
326     assert(bs != NULL);
327     assert(bs->socket != -1);
328     assert(bs->fd < 0);
329
330     bs->fd = stream_accept(bs->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
331     if (bs->fd < 0) {
332         security_stream_seterror(&bs->secstr,
333             "can't accept new stream connection: %s", strerror(errno));
334         return (-1);
335     }
336 #endif /* !TEST */                                              /* } */
337     return (0);
338 }
339
340 /*
341  * Return a connected stream
342  */
343 static void *
344 bsd_stream_client(
345     void *      h,
346     int         id)
347 {
348     struct sec_stream *bs = NULL;
349 #ifndef TEST                                                    /* { */
350     struct sec_handle *bh = h;
351 #ifdef DUMPER_SOCKET_BUFFERING
352     size_t rcvbuf = SIZEOF(bs->databuf) * 2;
353 #endif
354
355     assert(bh != NULL);
356
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);
361     if (bs->fd < 0) {
362         security_seterror(&bh->sech,
363             "can't connect stream to %s port %d: %s", bh->hostname,
364             id, strerror(errno));
365         amfree(bs);
366         return (NULL);
367     }
368     bs->socket = -1;    /* we're a client */
369     bs->ev_read = NULL;
370 #ifdef DUMPER_SOCKET_BUFFERING
371     setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, SIZEOF(rcvbuf));
372 #endif
373 #endif /* !TEST */                                              /* } */
374     return (bs);
375 }
376
377 /*
378  * Close and unallocate resources for a stream
379  */
380 static void
381 bsd_stream_close(
382     void *      s)
383 {
384     struct sec_stream *bs = s;
385
386     assert(bs != NULL);
387
388     if (bs->fd != -1)
389         aclose(bs->fd);
390     if (bs->socket != -1)
391         aclose(bs->socket);
392     bsd_stream_read_cancel(bs);
393     amfree(bs);
394 }
395
396 /*
397  * Authenticate a stream.  bsd streams have no authentication
398  */
399 static int
400 bsd_stream_auth(
401     void *      s)
402 {
403     (void)s;            /* Quiet unused parameter warning */
404
405     return (0); /* success */
406 }
407
408 /*
409  * Returns the stream id for this stream.  This is just the local port.
410  */
411 static int
412 bsd_stream_id(
413     void *      s)
414 {
415     struct sec_stream *bs = s;
416
417     assert(bs != NULL);
418
419     return ((int)bs->port);
420 }
421
422 /*
423  * Submit a request to read some data.  Calls back with the given function
424  * and arg when completed.
425  */
426 static void
427 bsd_stream_read(
428     void *      s,
429     void        (*fn)(void *, void *, ssize_t),
430     void *      arg)
431 {
432     struct sec_stream *bs = s;
433
434     /*
435      * Only one read request can be active per stream.
436      */
437     if (bs->ev_read != NULL)
438         event_release(bs->ev_read);
439
440     bs->ev_read = event_register((event_id_t)bs->fd, EV_READFD, stream_read_callback, bs);
441     bs->fn = fn;
442     bs->arg = arg;
443 }
444
445 /*
446  * Read a chunk of data to a stream.  Blocks until completion.
447  */
448 static ssize_t
449 bsd_stream_read_sync(
450     void *      s,
451     void **     buf)
452 {
453     struct sec_stream *bs = s;
454
455     assert(bs != NULL);
456
457     /*
458      * Only one read request can be active per stream.
459      */
460     if(bs->ev_read != NULL) {
461         return -1;
462     }
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);
466     *buf = bs->databuf;
467     return (bs->len);
468 }
469
470
471 /*
472  * Callback for bsd_stream_read_sync
473  */
474 static void
475 stream_read_sync_callback(
476     void *      s)
477 {
478     struct sec_stream *bs = s;
479     ssize_t n;
480
481     assert(bs != NULL);
482
483     bsdprintf(("%s: bsd: stream_read_callback_sync: fd %d\n",
484                  debug_prefix_time(NULL), bs->fd));
485
486     /*
487      * Remove the event first, in case they reschedule it in the callback.
488      */
489     bsd_stream_read_cancel(bs);
490     do {
491         n = read(bs->fd, bs->databuf, sizeof(bs->databuf));
492     } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
493     if (n < 0)
494         security_stream_seterror(&bs->secstr, strerror(errno));
495     bs->len = n;
496 }
497
498 /*
499  * Cancel a previous stream read request.  It's ok if we didn't
500  * have a read scheduled.
501  */
502 static void
503 bsd_stream_read_cancel(
504     void *      s)
505 {
506     struct sec_stream *bs = s;
507
508     assert(bs != NULL);
509     if (bs->ev_read != NULL) {
510         event_release(bs->ev_read);
511         bs->ev_read = NULL;
512     }
513 }
514
515 /*
516  * Callback for bsd_stream_read
517  */
518 static void
519 stream_read_callback(
520     void *      arg)
521 {
522     struct sec_stream *bs = arg;
523     ssize_t n;
524
525     assert(bs != NULL);
526
527     /*
528      * Remove the event first, in case they reschedule it in the callback.
529      */
530     bsd_stream_read_cancel(bs);
531     do {
532         n = read(bs->fd, bs->databuf, SIZEOF(bs->databuf));
533     } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
534
535     if (n < 0)
536         security_stream_seterror(&bs->secstr, strerror(errno));
537
538     (*bs->fn)(bs->arg, bs->databuf, n);
539 }
540
541 #endif  /* BSD_SECURITY */                                      /* } */
542
543 #if defined(TEST)                                               /* { */
544
545 /*
546  * The following dummy bind_portrange function is so we do not need to
547  * drag in util.o just for the test program.
548  */
549 int
550 bind_portrange(
551     int                 s,
552     struct sockaddr_in *addrp,
553     in_port_t           first_port,
554     in_port_t           last_port,
555     char *              proto)
556 {
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 */
562
563     return 0;
564 }
565
566 /*
567  * Construct a datestamp (YYYYMMDD) from a time_t.
568  */
569 char *
570 construct_datestamp(
571     time_t *    t)
572 {
573     struct tm *tm;
574     char datestamp[3*NUM_STR_SIZE];
575     time_t when;
576
577     if(t == NULL) {
578         when = time((time_t *)NULL);
579     } else {
580         when = *t;
581     }
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);
586 }
587
588 /*
589  * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t.
590  */
591 char *
592 construct_timestamp(
593     time_t *    t)
594 {
595     struct tm *tm;
596     char timestamp[6*NUM_STR_SIZE];
597     time_t when;
598
599     if(t == NULL) {
600         when = time((time_t *)NULL);
601     } else {
602         when = *t;
603     }
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);
610 }
611
612 /*
613  * The following are so we can include security.o but not all the rest
614  * of the security modules.
615  */
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 = {};
622
623 /*
624  * This function will be called to accept the connection and is used
625  * to report success or failure.
626  */
627 static void fake_accept_function(
628     security_handle_t * handle,
629     pkt_t *             pkt)
630 {
631     if (pkt == NULL) {
632         fputs(handle->error, stdout);
633         fputc('\n', stdout);
634     } else {
635         fputs("access is allowed\n", stdout);
636     }
637 }
638
639 int
640 main (
641     int         argc,
642     char **     argv)
643 {
644     char *remoteuser;
645     char *remotehost;
646     struct hostent *hp;
647     struct sec_handle *bh;
648     void *save_cur;
649     struct passwd *pwent;
650
651     /* Don't die when child closes pipe */
652     signal(SIGPIPE, SIG_IGN);
653
654     /*
655      * The following is stolen from amandad to emulate what it would
656      * do on startup.
657      */
658     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
659         client_uid = pwent->pw_uid;
660         client_gid = pwent->pw_gid;
661         endpwent();
662     }
663
664 #ifdef FORCE_USERID
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);
669             /*NOTREACHED*/
670         }
671         initgroups(CLIENT_LOGIN, client_gid);
672         setgid(client_gid);
673         setegid(client_gid);
674         seteuid(client_uid);
675     }
676 #endif  /* FORCE_USERID */
677
678     if (isatty(0)) {
679         fputs("Remote user: ", stdout);
680         fflush(stdout);
681     }
682     do {
683         amfree(remoteuser);
684         remoteuser = agets(stdin);
685         if (remoteuser == NULL)
686             return 0;
687     } while (remoteuser[0] == '\0');
688
689     if (isatty(0)) {
690         fputs("Remote host: ", stdout);
691         fflush(stdout);
692     }
693
694     do {
695         amfree(remotehost);
696         remotehost = agets(stdin);
697         if (remotehost == NULL)
698             return 0;
699     } while (remotehost[0] == '\0');
700
701     set_pname("security");
702     dbopen(NULL);
703
704     startclock();
705
706     if ((hp = gethostbyname(remotehost)) == NULL) {
707         fprintf(stderr, "cannot look up remote host %s\n", remotehost);
708         return 1;
709     }
710     memcpy((char *)&netfd.peer.sin_addr,
711            (char *)hp->h_addr,
712            SIZEOF(hp->h_addr));
713     /*
714      * Fake that it is coming from a reserved port.
715      */
716     netfd.peer.sin_port = htons(IPPORT_RESERVED - 1);
717
718     bh = alloc(SIZEOF(*bh));
719     bh->proto_handle=NULL;
720     bh->udp = &netfd;
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 */
727
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);
732
733     return 0;
734 }
735
736 #endif                                                          /* } */