60e63d3eecf49868d0f494838892fe2a8c15bfc4
[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.54 2006/03/09 16:51:41 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 "stream.h"
40 #include "version.h"
41
42 /*#define       BSD_DEBUG*/
43
44 #ifdef BSD_DEBUG
45 #define bsdprintf(x)    dbprintf(x)
46 #else
47 #define bsdprintf(x)
48 #endif
49
50 #ifndef SO_RCVBUF
51 #undef DUMPER_SOCKET_BUFFERING
52 #endif
53
54 #ifdef BSD_SECURITY                                             /* { */
55
56 /*
57  * Change the following from #undef to #define to cause detailed logging
58  * of the security steps, e.g. into /tmp/amanda/amandad*debug.
59  */
60 #undef SHOW_SECURITY_DETAIL
61
62 #if defined(TEST)                                               /* { */
63 #define SHOW_SECURITY_DETAIL
64 #undef bsdprintf
65 #define bsdprintf(p)    printf p
66 #endif                                                          /* } */
67
68 /*
69  * This is the private handle data
70  */
71 struct bsd_handle {
72     /*
73      * This must be first.  Instances of bsd_handle will be cast to
74      * security_handle_t's.
75      */
76     security_handle_t sech;
77
78     /*
79      * protocol handle for this request.  Each request gets its own
80      * handle, so we differentiate packets for them with a "handle" header
81      * in each packet.
82      *
83      */
84     int event_id; /* unique event_id */
85     char *proto_handle;
86
87     /*
88      * sequence number.
89      */
90     int sequence;
91
92     /*
93      * The remote host we're transmitting to
94      */
95     char hostname[256];
96     struct sockaddr_in peer;
97
98     /*
99      * Function to call when recvpkt detects new incoming data for this
100      * handle
101      */
102     void (*fn) P((void *, pkt_t *, security_status_t));
103
104     /*
105      * Argument for previous function
106      */
107     void *arg;
108
109     /*
110      * read (EV_WAIT) handle for a recv
111      */
112     event_handle_t *ev_read;
113
114     /*
115      * Timeout handle for a recv
116      */
117     event_handle_t *ev_timeout;
118
119     struct bsd_handle *prev, *next;
120 };
121
122 struct bsd_handle *bh_first=NULL, *bh_last=NULL;
123
124 /*
125  * This is the internal security_stream data
126  */
127 struct bsd_stream {
128     /*
129      * This must be first, because instances of this will be cast
130      * to security_stream_t's outside of this module.
131      */
132     security_stream_t secstr;
133
134     /*
135      * This is the file descriptor which we will do io on
136      */
137     int fd;
138
139     /*
140      * This is the file descriptor which we will listen for incoming
141      * connections on (for streams which receive connections)
142      */
143     int socket;
144
145     /*
146      * This is the local port this stream is bound to
147      */
148     int port;
149
150     /*
151      * This is the read event handle for this data stream
152      */
153     event_handle_t *ev_read;
154
155     /*
156      * This is the function and argument that is called when this stream
157      * is readable.  It is passed a buffer of data read.
158      */
159     void (*fn) P((void *, void *, ssize_t));
160     void *arg;
161
162     /*
163      * This is the buffer that we read data into that will be passed
164      * to the read callback.
165      */
166     char databuf[NETWORK_BLOCK_BYTES];
167 };
168
169 /*
170  * Interface functions
171  */
172 static void bsd_connect P((const char *,
173     char *(*)(char *, void *), 
174     void (*)(void *, security_handle_t *, security_status_t), void *));
175 static void bsd_accept P((int, int, void (*)(security_handle_t *, pkt_t *)));
176 static void bsd_close P((void *));
177 static int bsd_sendpkt P((void *, pkt_t *));
178 static void bsd_recvpkt P((void *,
179     void (*)(void *, pkt_t *, security_status_t), void *, int));
180 static void bsd_recvpkt_cancel P((void *));
181
182 static void *bsd_stream_server P((void *));
183 static int bsd_stream_accept P((void *));
184 static void *bsd_stream_client P((void *, int));
185 static void bsd_stream_close P((void *));
186 static int bsd_stream_auth P((void *));
187 static int bsd_stream_id P((void *));
188 static int bsd_stream_write P((void *, const void *, size_t));
189 static void bsd_stream_read P((void *, void (*)(void *, void *, ssize_t),
190     void *));
191 static void bsd_stream_read_cancel P((void *));
192
193 /*
194  * This is our interface to the outside world
195  */
196 const security_driver_t bsd_security_driver = {
197     "BSD",
198     bsd_connect,
199     bsd_accept,
200     bsd_close,
201     bsd_sendpkt,
202     bsd_recvpkt,
203     bsd_recvpkt_cancel,
204     bsd_stream_server,
205     bsd_stream_accept,
206     bsd_stream_client,
207     bsd_stream_close,
208     bsd_stream_auth,
209     bsd_stream_id,
210     bsd_stream_write,
211     bsd_stream_read,
212     bsd_stream_read_cancel,
213 };
214
215 /*
216  * This is data local to the datagram socket.  We have one datagram
217  * per process, so it is global.
218  */
219 static struct {
220     dgram_t dgram;              /* datagram to read/write from */
221     struct sockaddr_in peer;    /* who sent it to us */
222     pkt_t pkt;                  /* parsed form of dgram */
223     char *handle;               /* handle from recvd packet */
224     int sequence;               /* seq no of packet */
225     event_handle_t *ev_read;    /* read event handle from dgram */
226     int refcnt;                 /* number of handles blocked for reading */
227 } netfd;
228
229 /* generate new handles from here */
230 static int newhandle = 0;
231 static int newevent = 0;
232
233 /*
234  * We register one event handler for our network fd which takes
235  * care of all of our async requests.  When all async requests
236  * have either been satisfied or cancelled, we unregister our
237  * network event handler.
238  */
239 #define netfd_addref()  do      {                                       \
240     if (netfd.refcnt++ == 0) {                                          \
241         assert(netfd.ev_read == NULL);                                  \
242         netfd.ev_read = event_register(netfd.dgram.socket, EV_READFD,   \
243             netfd_read_callback, NULL);                                 \
244     }                                                                   \
245     assert(netfd.refcnt > 0);                                           \
246 } while (0)
247
248 /*
249  * If this is the last request to be removed, then remove the
250  * reader event from the netfd.
251  */
252 #define netfd_delref()  do      {                                       \
253     assert(netfd.refcnt > 0);                                           \
254     if (--netfd.refcnt == 0) {                                          \
255         assert(netfd.ev_read != NULL);                                  \
256         event_release(netfd.ev_read);                                   \
257         netfd.ev_read = NULL;                                           \
258     }                                                                   \
259 } while (0)
260
261 /*
262  * This is the function and argument that is called when new requests
263  * arrive on the netfd.
264  */
265 static void (*accept_fn) P((security_handle_t *, pkt_t *));
266
267 /*
268  * These are the internal helper functions
269  */
270 static char *check_user P((struct bsd_handle *, const char *));
271 static int inithandle P((struct bsd_handle *, struct hostent *,
272                          int, char *, int));
273 static const char *pkthdr2str P((const struct bsd_handle *, const pkt_t *));
274 static int str2pkthdr P((void));
275 static void netfd_read_callback P((void *));
276 static void recvpkt_callback P((void *));
277 static void recvpkt_timeout P((void *));
278 static int recv_security_ok P((struct bsd_handle *));
279 static void stream_read_callback P((void *));
280
281
282 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
283 /*
284  * Display stat() information about a file.
285  */
286 void show_stat_info(a, b)
287     char *a, *b;
288 {
289     char *name = vstralloc(a, b, NULL);
290     struct stat sbuf;
291     struct passwd *pwptr;
292     char *owner;
293     struct group *grptr;
294     char *group;
295
296     if (stat(name, &sbuf) != 0) {
297         bsdprintf(("%s: cannot stat %s: %s\n",
298                    debug_prefix_time(NULL), name, strerror(errno)));
299         amfree(name);
300         return;
301     }
302     if ((pwptr = getpwuid(sbuf.st_uid)) == NULL) {
303         owner = alloc(NUM_STR_SIZE + 1);
304         snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_uid);
305     } else {
306         owner = stralloc(pwptr->pw_name);
307     }
308     if ((grptr = getgrgid(sbuf.st_gid)) == NULL) {
309         group = alloc(NUM_STR_SIZE + 1);
310         snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_gid);
311     } else {
312         group = stralloc(grptr->gr_name);
313     }
314     bsdprintf(("%s: processing file: %s\n", debug_prefix(NULL), name));
315     bsdprintf(("%s:                  owner=%s group=%s mode=%03o\n",
316                debug_prefix(NULL), owner, group, (int) (sbuf.st_mode & 0777)));
317     amfree(name);
318     amfree(owner);
319     amfree(group);
320 }
321 #endif                                                          /* } */
322
323 /*
324  * Setup and return a handle outgoing to a client
325  */
326 static void
327 bsd_connect(hostname, conf_fn, fn, arg)
328     const char *hostname;
329     char *(*conf_fn) P((char *, void *));
330     void (*fn) P((void *, security_handle_t *, security_status_t));
331     void *arg;
332 {
333     struct bsd_handle *bh;
334     struct servent *se;
335     struct hostent *he;
336     int port;
337     struct timeval sequence_time;
338     amanda_timezone dontcare;
339     int sequence;
340     char *handle;
341
342     assert(hostname != NULL);
343
344     bh = alloc(sizeof(*bh));
345     bh->proto_handle=NULL;
346     security_handleinit(&bh->sech, &bsd_security_driver);
347
348     /*
349      * Only init the socket once
350      */
351     if (netfd.dgram.socket == 0) {
352         uid_t euid;
353         dgram_zero(&netfd.dgram);
354         
355         euid = geteuid();
356         seteuid(0);
357         dgram_bind(&netfd.dgram, &port);
358         seteuid(euid);
359         /*
360          * We must have a reserved port.  Bomb if we didn't get one.
361          */
362         if (port >= IPPORT_RESERVED) {
363             security_seterror(&bh->sech,
364                 "unable to bind to a reserved port (got port %d)",
365                 port);
366             (*fn)(arg, &bh->sech, S_ERROR);
367             return;
368         }
369     }
370
371     if ((he = gethostbyname(hostname)) == NULL) {
372         security_seterror(&bh->sech,
373             "%s: could not resolve hostname", hostname);
374         (*fn)(arg, &bh->sech, S_ERROR);
375         return;
376     }
377     if ((se = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL)
378         port = htons(AMANDA_SERVICE_DEFAULT);
379     else
380         port = se->s_port;
381     amanda_gettimeofday(&sequence_time, &dontcare);
382     sequence = (int)sequence_time.tv_sec ^ (int)sequence_time.tv_usec;
383     handle=malloc(15);
384     snprintf(handle,14,"000-%08x", newhandle++);
385     if (inithandle(bh, he, port, handle, sequence) < 0)
386         (*fn)(arg, &bh->sech, S_ERROR);
387     else
388         (*fn)(arg, &bh->sech, S_OK);
389 }
390
391 /*
392  * Setup to accept new incoming connections
393  */
394 static void
395 bsd_accept(in, out, fn)
396     int in, out;
397     void (*fn) P((security_handle_t *, pkt_t *));
398 {
399
400     assert(in >= 0 && out >= 0);
401     assert(fn != NULL);
402
403     /*
404      * We assume in and out point to the same socket, and just use
405      * in.
406      */
407     dgram_socket(&netfd.dgram, in);
408
409     /*
410      * Assign the function and return.  When they call recvpkt later,
411      * the recvpkt callback will call this function when it discovers
412      * new incoming connections
413      */
414     accept_fn = fn;
415
416     netfd_addref();
417 }
418
419 /*
420  * Given a hostname and a port, setup a bsd_handle
421  */
422 static int
423 inithandle(bh, he, port, handle, sequence)
424     struct bsd_handle *bh;
425     struct hostent *he;
426     int port;
427     char *handle;
428     int sequence;
429 {
430     int i;
431
432     assert(he != NULL);
433     assert(port > 0);
434
435     /*
436      * Save the hostname and port info
437      */
438     strncpy(bh->hostname, he->h_name, sizeof(bh->hostname) - 1);
439     bh->hostname[sizeof(bh->hostname) - 1] = '\0';
440     bh->peer.sin_addr = *(struct in_addr *)he->h_addr;
441     bh->peer.sin_port = port;
442     bh->peer.sin_family = AF_INET;
443
444     bh->prev = bh_last;
445     if(bh_last) {bh->prev->next = bh;}
446     if(!bh_first) {bh_first = bh;}
447     bh->next = NULL;
448     bh_last = bh;
449
450     /*
451      * Do a forward lookup of the hostname.  This is unnecessary if we
452      * are initiating the connection, but is very serious if we are
453      * receiving.  We want to make sure the hostname
454      * resolves back to the remote ip for security reasons.
455      */
456     if ((he = gethostbyname(bh->hostname)) == NULL) {
457         security_seterror(&bh->sech,
458             "%s: could not resolve hostname", bh->hostname);
459         return (-1);
460     }
461     /*
462      * Make sure the hostname matches.  This should always work.
463      */
464     if (strncasecmp(bh->hostname, he->h_name, strlen(bh->hostname)) != 0) {
465         security_seterror(&bh->sech,
466             "%s: did not resolve to %s", bh->hostname, bh->hostname);
467         return (-1);
468     }
469
470     /*
471      * Now look for a matching ip address.
472      */
473     for (i = 0; he->h_addr_list[i] != NULL; i++) {
474         if (memcmp(&bh->peer.sin_addr, he->h_addr_list[i],
475             sizeof(struct in_addr)) == 0) {
476             break;
477         }
478     }
479
480     /*
481      * If we didn't find it, try the aliases.  This is a workaround for
482      * Solaris if DNS goes over NIS.
483      */
484     if (he->h_addr_list[i] == NULL) {
485         const char *ipstr = inet_ntoa(bh->peer.sin_addr);
486         for (i = 0; he->h_aliases[i] != NULL; i++) {
487             if (strcmp(he->h_aliases[i], ipstr) == 0)
488                 break;
489         }
490         /*
491          * No aliases either.  Failure.  Someone is fooling with us or
492          * DNS is messed up.
493          */
494         if (he->h_aliases[i] == NULL) {
495             security_seterror(&bh->sech,
496                 "DNS check failed: no matching ip address for %s",
497                 bh->hostname);
498             return (-1);
499         }
500     }
501
502     bh->sequence = sequence;
503     bh->event_id = newevent++;
504     bh->proto_handle = handle;
505     bh->fn = NULL;
506     bh->arg = NULL;
507     bh->ev_read = NULL;
508     bh->ev_timeout = NULL;
509
510     bsdprintf(("%s: adding handle '%s'\n",
511                debug_prefix_time(NULL), bh->proto_handle));
512
513     return(0);
514 }
515
516 /*
517  * Frees a handle allocated by the above
518  */
519 static void
520 bsd_close(cookie)
521     void *cookie;
522 {
523     struct bsd_handle *bh = cookie;
524
525     if(bh->proto_handle == NULL) {
526         return;
527     }
528
529     bsdprintf(("%s: close handle '%s'\n",
530                debug_prefix_time(NULL), bh->proto_handle));
531
532     bsd_recvpkt_cancel(bh);
533     if(bh->next) {
534         bh->next->prev = bh->prev;
535     }
536     else {
537         bh_last = bh->prev;
538     }
539     if(bh->prev) {
540         bh->prev->next = bh->next;
541     }
542     else {
543         bh_first = bh->next;
544     }
545
546     amfree(bh);
547 }
548
549 /*
550  * Transmit a packet.  Add security information first.
551  */
552 static int
553 bsd_sendpkt(cookie, pkt)
554     void *cookie;
555     pkt_t *pkt;
556 {
557     struct bsd_handle *bh = cookie;
558     struct passwd *pwd;
559
560     assert(bh != NULL);
561     assert(pkt != NULL);
562
563     /*
564      * Initialize this datagram, and add the header
565      */
566     dgram_zero(&netfd.dgram);
567     dgram_cat(&netfd.dgram, pkthdr2str(bh, pkt));
568
569     /*
570      * Add the security info.  This depends on which kind of packet we're
571      * sending.
572      */
573     switch (pkt->type) {
574     case P_REQ:
575         /*
576          * Requests get sent with our username in the body
577          */
578         if ((pwd = getpwuid(geteuid())) == NULL) {
579             security_seterror(&bh->sech,
580                 "can't get login name for my uid %ld", (long)getuid());
581             return (-1);
582         }
583         dgram_cat(&netfd.dgram, "SECURITY USER %s\n", pwd->pw_name);
584         break;
585
586     default:
587         break;
588     }
589
590     /*
591      * Add the body, and send it
592      */
593     dgram_cat(&netfd.dgram, pkt->body);
594     if (dgram_send_addr(bh->peer, &netfd.dgram) != 0) {
595         security_seterror(&bh->sech,
596             "send %s to %s failed: %s", pkt_type2str(pkt->type),
597             bh->hostname, strerror(errno));
598         return (-1);
599     }
600     return (0);
601 }
602
603 /*
604  * Set up to receive a packet asynchronously, and call back when it has
605  * been read.
606  */
607 static void
608 bsd_recvpkt(cookie, fn, arg, timeout)
609     void *cookie, *arg;
610     void (*fn) P((void *, pkt_t *, security_status_t));
611     int timeout;
612 {
613     struct bsd_handle *bh = cookie;
614
615     assert(bh != NULL);
616     assert(fn != NULL);
617
618
619     /*
620      * Subsequent recvpkt calls override previous ones
621      */
622     if (bh->ev_read == NULL) {
623         netfd_addref();
624         bh->ev_read = event_register(bh->event_id, EV_WAIT,
625             recvpkt_callback, bh);
626     }
627     if (bh->ev_timeout != NULL)
628         event_release(bh->ev_timeout);
629     if (timeout < 0)
630         bh->ev_timeout = NULL;
631     else
632         bh->ev_timeout = event_register(timeout, EV_TIME, recvpkt_timeout, bh);
633     bh->fn = fn;
634     bh->arg = arg;
635 }
636
637 /*
638  * Remove a async receive request on this handle from the queue.
639  * If it is the last one to be removed, then remove the event
640  * handler for our network fd
641  */
642 static void
643 bsd_recvpkt_cancel(cookie)
644     void *cookie;
645 {
646     struct bsd_handle *bh = cookie;
647
648     assert(bh != NULL);
649
650     if (bh->ev_read != NULL) {
651         netfd_delref();
652         event_release(bh->ev_read);
653         bh->ev_read = NULL;
654     }
655
656     if (bh->ev_timeout != NULL) {
657         event_release(bh->ev_timeout);
658         bh->ev_timeout = NULL;
659     }
660 }
661
662 /*
663  * Callback for received packets.  This is the function bsd_recvpkt
664  * registers with the event handler.  It is called when the event handler
665  * realizes that data is waiting to be read on the network socket.
666  */
667 static void
668 netfd_read_callback(cookie)
669     void *cookie;
670 {
671     struct bsd_handle *bh;
672     struct hostent *he;
673     int a;
674
675     assert(cookie == NULL);
676
677 #ifndef TEST                                                    /* { */
678     /*
679      * Receive the packet.
680      */
681     dgram_zero(&netfd.dgram);
682     if (dgram_recv(&netfd.dgram, 0, &netfd.peer) < 0)
683         return;
684 #endif /* !TEST */                                              /* } */
685
686     /*
687      * Parse the packet.
688      */
689     if (str2pkthdr() < 0)
690         return;
691
692     /*
693      * If there are events waiting on this handle, we're done
694      */
695     bh = bh_first;
696     while(bh != NULL && (strcmp(bh->proto_handle, netfd.handle) != 0 ||
697                          bh->sequence != netfd.sequence ||
698                          bh->peer.sin_addr.s_addr != netfd.peer.sin_addr.s_addr ||
699                          bh->peer.sin_port != netfd.peer.sin_port)) {
700         bh = bh->next;
701     }
702     if (bh && event_wakeup(bh->event_id) > 0)
703         return;
704
705     /*
706      * If we didn't find a handle, then check for a new incoming packet.
707      * If no accept handler was setup, then just return.
708      */
709     if (accept_fn == NULL)
710         return;
711
712     he = gethostbyaddr((void *)&netfd.peer.sin_addr,
713         (int)sizeof(netfd.peer.sin_addr), AF_INET);
714     if (he == NULL)
715         return;
716     bh = alloc(sizeof(*bh));
717     bh->proto_handle=NULL;
718     security_handleinit(&bh->sech, &bsd_security_driver);
719     a = inithandle(bh,
720                    he,
721                    netfd.peer.sin_port,
722                    netfd.handle,
723                    netfd.sequence);
724     if (a < 0) {
725         if(bh->next) {
726             bh->next->prev = bh->prev;
727         }
728         else {
729             bh_last = bh->prev;
730         }
731         if(bh->prev) {
732             bh->prev->next = bh->next;
733         }
734         else {
735             bh_first = bh->prev;
736         }
737
738         bsdprintf(("%s: closeX handle '%s'\n",
739                   debug_prefix_time(NULL), bh->proto_handle));
740
741         amfree(bh);
742         return;
743     }
744     /*
745      * Check the security of the packet.  If it is bad, then pass NULL
746      * to the accept function instead of a packet.
747      */
748     if (recv_security_ok(bh) < 0)
749         (*accept_fn)(&bh->sech, NULL);
750     else
751         (*accept_fn)(&bh->sech, &netfd.pkt);
752 }
753
754 /*
755  * This is called when a handle is woken up because data read off of the
756  * net is for it.
757  */
758 static void
759 recvpkt_callback(cookie)
760     void *cookie;
761 {
762     struct bsd_handle *bh = cookie;
763     void (*fn) P((void *, pkt_t *, security_status_t));
764     void *arg;
765
766     assert(bh != NULL);
767     bsdprintf(("%s: receive handle '%s' netfd '%s'\n",
768                debug_prefix_time(NULL), bh->proto_handle,netfd.handle));
769
770     if(strcmp(bh->proto_handle,netfd.handle) != 0) assert(1);
771
772     /* if it didn't come from the same host/port, forget it */
773     if (memcmp(&bh->peer.sin_addr, &netfd.peer.sin_addr,
774         sizeof(netfd.peer.sin_addr)) != 0 ||
775         bh->peer.sin_port != netfd.peer.sin_port) {
776         netfd.handle = NULL;
777         return;
778     }
779
780     /*
781      * We need to cancel the recvpkt request before calling the callback
782      * because the callback may reschedule us.
783      */
784     fn = bh->fn;
785     arg = bh->arg;
786     bsd_recvpkt_cancel(bh);
787
788     /*
789      * Check the security of the packet.  If it is bad, then pass NULL
790      * to the packet handling function instead of a packet.
791      */
792     if (recv_security_ok(bh) < 0)
793         (*fn)(arg, NULL, S_ERROR);
794     else
795         (*fn)(arg, &netfd.pkt, S_OK);
796 }
797
798 /*
799  * This is called when a handle times out before receiving a packet.
800  */
801 static void
802 recvpkt_timeout(cookie)
803     void *cookie;
804 {
805     struct bsd_handle *bh = cookie;
806     void (*fn) P((void *, pkt_t *, security_status_t));
807     void *arg;
808
809     assert(bh != NULL);
810
811     assert(bh->ev_timeout != NULL);
812     fn = bh->fn;
813     arg = bh->arg;
814     bsd_recvpkt_cancel(bh);
815     (*fn)(arg, NULL, S_TIMEOUT);
816
817 }
818
819 /*
820  * Check the security of a received packet.  Returns negative on security
821  * violation, or returns 0 if ok.  Removes the security info from the pkt_t.
822  */
823 static int
824 recv_security_ok(bh)
825     struct bsd_handle *bh;
826 {
827     char *tok, *security, *body, *result;
828     pkt_t *pkt = &netfd.pkt;
829
830     /*
831      * Set this preempively before we mangle the body.  
832      */
833     security_seterror(&bh->sech,
834         "bad SECURITY line: '%s'", pkt->body);
835
836     /*
837      * Now, find the SECURITY line in the body, and parse it out
838      * into an argv.
839      */
840     if (strncmp(pkt->body, "SECURITY", sizeof("SECURITY") - 1) == 0) {
841         tok = strtok(pkt->body, " ");
842         assert(strcmp(tok, "SECURITY") == 0);
843         /* security info goes until the newline */
844         security = strtok(NULL, "\n");
845         body = strtok(NULL, "");
846         /*
847          * If the body is f-ked, then try to recover
848          */
849         if (body == NULL) {
850             if (security != NULL)
851                 body = security + strlen(security) + 2;
852             else
853                 body = pkt->body;
854         }
855     } else {
856         security = NULL;
857         body = pkt->body;
858     }
859
860     /*
861      * We need to do different things depending on which type of packet
862      * this is.
863      */
864     switch (pkt->type) {
865     case P_REQ:
866         /*
867          * Request packets must come from a reserved port
868          */
869         if (ntohs(bh->peer.sin_port) >= IPPORT_RESERVED) {
870             security_seterror(&bh->sech,
871                 "host %s: port %d not secure", bh->hostname,
872                 ntohs(bh->peer.sin_port));
873             return (-1);
874         }
875
876         /*
877          * Request packets contain a remote username.  We need to check
878          * that we allow it in.
879          *
880          * They will look like:
881          *      SECURITY USER [username]
882          */
883
884         /* there must be some security info */
885         if (security == NULL) {
886             security_seterror(&bh->sech,
887                 "no bsd SECURITY for P_REQ");
888             return (-1);
889         }
890
891         /* second word must be USER */
892         if ((tok = strtok(security, " ")) == NULL)
893             return (-1);        /* default errmsg */
894         if (strcmp(tok, "USER") != 0) {
895             security_seterror(&bh->sech,
896                 "REQ SECURITY line parse error, expecting USER, got %s", tok);
897             return (-1);
898         }
899
900         /* the third word is the username */
901         if ((tok = strtok(NULL, "")) == NULL)
902             return (-1);        /* default errmsg */
903         if ((result = check_user(bh, tok)) != NULL) {
904             security_seterror(&bh->sech, "%s", result);
905             amfree(result);
906             return (-1);
907         }
908
909         /* we're good to go */
910         break;
911     default:
912         break;
913     }
914
915     /*
916      * If there is security info at the front of the packet, we need to
917      * shift the rest of the data up and nuke it.
918      */
919     if (body != pkt->body)
920         memmove(pkt->body, body, strlen(body) + 1);
921     return (0);
922 }
923
924 static char *
925 check_user(bh, remoteuser)
926     struct bsd_handle *bh;
927     const char *remoteuser;
928 {
929     struct passwd *pwd;
930     char *r;
931     char *result = NULL;
932     char *localuser = NULL;
933
934     /* lookup our local user name */
935     if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
936         return vstralloc("getpwnam(", CLIENT_LOGIN, ") fails", NULL);
937     }
938
939     /*
940      * Make a copy of the user name in case getpw* is called by
941      * any of the lower level routines.
942      */
943     localuser = stralloc(pwd->pw_name);
944
945 #ifndef USE_AMANDAHOSTS
946     r = check_user_ruserok(bh->hostname, pwd, remoteuser);
947 #else
948     r = check_user_amandahosts(bh->hostname, pwd, remoteuser);
949 #endif
950     if (r != NULL) {
951         result = vstralloc("access as ", localuser, " not allowed",
952                            " from ", remoteuser, "@", bh->hostname,
953                            ": ", r,
954                            NULL);
955         amfree(r);
956     }
957     amfree(localuser);
958     return result;
959 }
960
961 /*
962  * See if a remote user is allowed in.  This version uses ruserok()
963  * and friends.
964  *
965  * Returns 0 on success, or negative on error.
966  */
967 char *
968 check_user_ruserok(host, pwd, remoteuser)
969     const char *host;
970     struct passwd *pwd;
971     const char *remoteuser;
972 {
973     int saved_stderr;
974     int fd[2];
975     FILE *fError;
976     amwait_t exitcode;
977     pid_t ruserok_pid;
978     pid_t pid;
979     char *es;
980     char *result;
981     int ok;
982     char number[NUM_STR_SIZE];
983     uid_t myuid = getuid();
984
985     /*
986      * note that some versions of ruserok (eg SunOS 3.2) look in
987      * "./.rhosts" rather than "~CLIENT_LOGIN/.rhosts", so we have to
988      * chdir ourselves.  Sigh.
989      *
990      * And, believe it or not, some ruserok()'s try an initgroup just
991      * for the hell of it.  Since we probably aren't root at this point
992      * it'll fail, and initgroup "helpfully" will blatt "Setgroups: Not owner"
993      * into our stderr output even though the initgroup failure is not a
994      * problem and is expected.  Thanks a lot.  Not.
995      */
996     if (pipe(fd) != 0) {
997         return stralloc2("pipe() fails: ", strerror(errno));
998     }
999     if ((ruserok_pid = fork()) < 0) {
1000         return stralloc2("fork() fails: ", strerror(errno));
1001     } else if (ruserok_pid == 0) {
1002         int ec;
1003
1004         close(fd[0]);
1005         fError = fdopen(fd[1], "w");
1006         /* pamper braindead ruserok's */
1007         if (chdir(pwd->pw_dir) != 0) {
1008             fprintf(fError, "chdir(%s) failed: %s",
1009                     pwd->pw_dir, strerror(errno));
1010             fclose(fError);
1011             exit(1);
1012         }
1013
1014 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
1015         {
1016         char *dir = stralloc(pwd->pw_dir);
1017
1018         bsdprintf(("%s: calling ruserok(%s, %d, %s, %s)\n",
1019                    debug_prefix_time(NULL),
1020                    host, myuid == 0, remoteuser, pwd->pw_name));
1021         if (myuid == 0) {
1022             bsdprintf(("%s: because you are running as root, ",
1023                        debug_prefix(NULL)));
1024             bsdprintf(("/etc/hosts.equiv will not be used\n"));
1025         } else {
1026             show_stat_info("/etc/hosts.equiv", NULL);
1027         }
1028         show_stat_info(dir, "/.rhosts");
1029         amfree(dir);
1030         }
1031 #endif                                                          /* } */
1032
1033         saved_stderr = dup(2);
1034         close(2);
1035         if (open("/dev/null", O_RDWR) == -1) {
1036             dbprintf(("Could not open /dev/null: %s\n",
1037                       strerror(errno)));
1038             ec = 1;
1039         } else {
1040             ok = ruserok(host, myuid == 0, remoteuser, CLIENT_LOGIN);
1041             if (ok < 0) {
1042                 ec = 1;
1043             } else {
1044                 ec = 0;
1045             }
1046         }
1047         (void)dup2(saved_stderr,2);
1048         close(saved_stderr);
1049         exit(ec);
1050     }
1051     close(fd[1]);
1052     fError = fdopen(fd[0], "r");
1053
1054     result = NULL;
1055     while ((es = agets(fError)) != NULL) {
1056         if (result == NULL) {
1057             result = stralloc("");
1058         } else {
1059             strappend(result, ": ");
1060         }
1061         strappend(result, es);
1062     }
1063     close(fd[0]);
1064
1065     while (1) {
1066         if ((pid = wait(&exitcode)) == (pid_t) -1) {
1067             if (errno == EINTR) {
1068                 continue;
1069             }
1070             amfree(result);
1071             return stralloc2("ruserok wait failed: %s", strerror(errno));
1072         }
1073         if (pid == ruserok_pid) {
1074             break;
1075         }
1076     }
1077     if (WIFSIGNALED(exitcode)) {
1078         amfree(result);
1079         snprintf(number, sizeof(number), "%d", WTERMSIG(exitcode));
1080         return stralloc2("ruserok child got signal ", number);
1081     }
1082     if (WEXITSTATUS(exitcode) == 0) {
1083         amfree(result);
1084     } else if (result == NULL) {
1085         result = stralloc("ruserok failed");
1086     }
1087
1088     return result;
1089 }
1090
1091 /*
1092  * Check to see if a user is allowed in.  This version uses .amandahosts
1093  * Returns -1 on failure, or 0 on success.
1094  */
1095 char *
1096 check_user_amandahosts(host, pwd, remoteuser)
1097     const char *host;
1098     struct passwd *pwd;
1099     const char *remoteuser;
1100 {
1101     char *line = NULL;
1102     char *filehost;
1103     const char *fileuser;
1104     char *ptmp = NULL;
1105     char *result = NULL;
1106     FILE *fp = NULL;
1107     int found;
1108     struct stat sbuf;
1109     char n1[NUM_STR_SIZE];
1110     char n2[NUM_STR_SIZE];
1111     int hostmatch;
1112     int usermatch;
1113     uid_t localuid;
1114     char *localuser = NULL;
1115
1116     /*
1117      * Save copies of what we need from the passwd structure in case
1118      * any other code calls getpw*.
1119      */
1120     localuid = pwd->pw_uid;
1121     localuser = stralloc(pwd->pw_name);
1122
1123     ptmp = stralloc2(pwd->pw_dir, "/.amandahosts");
1124 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
1125     show_stat_info(ptmp, "");;
1126 #endif                                                          /* } */
1127     if ((fp = fopen(ptmp, "r")) == NULL) {
1128         result = vstralloc("cannot open ", ptmp, ": ", strerror(errno), NULL);
1129         amfree(ptmp);
1130         amfree(localuser);
1131         return result;
1132     }
1133
1134     /*
1135      * Make sure the file is owned by the Amanda user and does not
1136      * have any group/other access allowed.
1137      */
1138     if (fstat(fileno(fp), &sbuf) != 0) {
1139         result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL);
1140         goto common_exit;
1141     }
1142     if (sbuf.st_uid != localuid) {
1143         snprintf(n1, sizeof(n1), "%ld", (long)sbuf.st_uid);
1144         snprintf(n2, sizeof(n2), "%ld", (long)localuid);
1145         result = vstralloc(ptmp, ": ",
1146                            "owned by id ", n1,
1147                            ", should be ", n2,
1148                            NULL);
1149         goto common_exit;
1150     }
1151     if ((sbuf.st_mode & 077) != 0) {
1152         result = stralloc2(ptmp,
1153           ": incorrect permissions; file must be accessible only by its owner");
1154         goto common_exit;
1155     }
1156
1157     /*
1158      * Now, scan the file for the host/user.
1159      */
1160     found = 0;
1161     while ((line = agets(fp)) != NULL) {
1162 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
1163         bsdprintf(("%s: processing line: <%s>\n", debug_prefix(NULL), line));
1164 #endif                                                          /* } */
1165         /* get the host out of the file */
1166         if ((filehost = strtok(line, " \t")) == NULL) {
1167             amfree(line);
1168             continue;
1169         }
1170
1171         /* get the username.  If no user specified, then use the local user */
1172         if ((fileuser = strtok(NULL, " \t")) == NULL) {
1173             fileuser = localuser;
1174         }
1175
1176         hostmatch = (strcasecmp(filehost, host) == 0);
1177         usermatch = (strcasecmp(fileuser, remoteuser) == 0);
1178 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
1179         bsdprintf(("%s: comparing \"%s\" with\n", debug_prefix(NULL), filehost));
1180         bsdprintf(("%s:           \"%s\" (%s)\n", host,
1181                   debug_prefix(NULL), hostmatch ? "match" : "no match"));
1182         bsdprintf(("%s:       and \"%s\" with\n", fileuser, debug_prefix(NULL)));
1183         bsdprintf(("%s:           \"%s\" (%s)\n", remoteuser,
1184                   debug_prefix(NULL), usermatch ? "match" : "no match"));
1185 #endif                                                          /* } */
1186         amfree(line);
1187         /* compare */
1188         if (hostmatch && usermatch) {
1189             /* success */
1190             found = 1;
1191             break;
1192         }
1193     }
1194     if (! found) {
1195         result = vstralloc(ptmp, ": ",
1196                            "\"", host, " ", remoteuser, "\"",
1197                            " entry not found",
1198                            NULL);
1199     }
1200
1201 common_exit:
1202
1203     afclose(fp);
1204     amfree(ptmp);
1205     amfree(line);
1206     amfree(localuser);
1207
1208     return result;
1209 }
1210
1211 /* return 1 on success, 0 on failure */
1212 int
1213 check_security(addr, str, cksum, errstr)
1214 struct sockaddr_in *addr;
1215 char *str;
1216 unsigned long cksum;
1217 char **errstr;
1218 {
1219     char *remotehost = NULL, *remoteuser = NULL;
1220     char *bad_bsd = NULL;
1221     struct hostent *hp;
1222     struct passwd *pwptr;
1223     int myuid, i, j;
1224     char *s, *fp;
1225     int ch;
1226
1227     *errstr = NULL;
1228
1229     /* what host is making the request? */
1230
1231     hp = gethostbyaddr((char *)&addr->sin_addr, sizeof(addr->sin_addr),
1232                        AF_INET);
1233     if(hp == NULL) {
1234         /* XXX include remote address in message */
1235         *errstr = vstralloc("[",
1236                             "addr ", inet_ntoa(addr->sin_addr), ": ",
1237                             "hostname lookup failed",
1238                             "]", NULL);
1239         return 0;
1240     }
1241     remotehost = stralloc(hp->h_name);
1242
1243     /* Now let's get the hostent for that hostname */
1244     hp = gethostbyname( remotehost );
1245     if(hp == NULL) {
1246         /* XXX include remote hostname in message */
1247         *errstr = vstralloc("[",
1248                             "host ", remotehost, ": ",
1249                             "hostname lookup failed",
1250                             "]", NULL);
1251         amfree(remotehost);
1252         return 0;
1253     }
1254
1255     /* Verify that the hostnames match -- they should theoretically */
1256     if( strncasecmp( remotehost, hp->h_name, strlen(remotehost)+1 ) != 0 ) {
1257         *errstr = vstralloc("[",
1258                             "hostnames do not match: ",
1259                             remotehost, " ", hp->h_name,
1260                             "]", NULL);
1261         amfree(remotehost);
1262         return 0;
1263     }
1264
1265     /* Now let's verify that the ip which gave us this hostname
1266      * is really an ip for this hostname; or is someone trying to
1267      * break in? (THIS IS THE CRUCIAL STEP)
1268      */
1269     for (i = 0; hp->h_addr_list[i]; i++) {
1270         if (memcmp(hp->h_addr_list[i],
1271                    (char *) &addr->sin_addr, sizeof(addr->sin_addr)) == 0)
1272             break;                     /* name is good, keep it */
1273     }
1274
1275     /* If we did not find it, your DNS is messed up or someone is trying
1276      * to pull a fast one on you. :(
1277      */
1278
1279    /*   Check even the aliases list. Work around for Solaris if dns goes over NIS */
1280
1281     if( !hp->h_addr_list[i] ) {
1282         for (j = 0; hp->h_aliases[j] !=0 ; j++) {
1283              if ( strcmp(hp->h_aliases[j],inet_ntoa(addr->sin_addr)) == 0)
1284                  break;                          /* name is good, keep it */
1285         }
1286         if( !hp->h_aliases[j] ) {
1287             *errstr = vstralloc("[",
1288                                 "ip address ", inet_ntoa(addr->sin_addr),
1289                                 " is not in the ip list for ", remotehost,
1290                                 "]",
1291                                 NULL);
1292             amfree(remotehost);
1293             return 0;
1294         }
1295     }
1296
1297     /* next, make sure the remote port is a "reserved" one */
1298
1299     if(ntohs(addr->sin_port) >= IPPORT_RESERVED) {
1300         char number[NUM_STR_SIZE];
1301
1302         snprintf(number, sizeof(number), "%d", ntohs(addr->sin_port));
1303         *errstr = vstralloc("[",
1304                             "host ", remotehost, ": ",
1305                             "port ", number, " not secure",
1306                             "]", NULL);
1307         amfree(remotehost);
1308         return 0;
1309     }
1310
1311     /* extract the remote user name from the message */
1312
1313     s = str;
1314     ch = *s++;
1315
1316     bad_bsd = vstralloc("[",
1317                         "host ", remotehost, ": ",
1318                         "bad bsd security line",
1319                         "]", NULL);
1320
1321 #define sc "USER "
1322     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1323         *errstr = bad_bsd;
1324         bad_bsd = NULL;
1325         amfree(remotehost);
1326         return 0;
1327     }
1328     s += sizeof(sc)-1;
1329     ch = s[-1];
1330 #undef sc
1331
1332     skip_whitespace(s, ch);
1333     if(ch == '\0') {
1334         *errstr = bad_bsd;
1335         bad_bsd = NULL;
1336         amfree(remotehost);
1337         return 0;
1338     }
1339     fp = s - 1;
1340     skip_non_whitespace(s, ch);
1341     s[-1] = '\0';
1342     remoteuser = stralloc(fp);
1343     s[-1] = ch;
1344     amfree(bad_bsd);
1345
1346     /* lookup our local user name */
1347
1348     myuid = getuid();
1349     if((pwptr = getpwuid(myuid)) == NULL)
1350         error("error [getpwuid(%d) fails]", myuid);
1351
1352     dbprintf(("bsd security: remote host %s user %s local user %s\n",
1353               remotehost, remoteuser, pwptr->pw_name));
1354
1355 #ifndef USE_AMANDAHOSTS
1356     s = check_user_ruserok(remotehost, pwptr, remoteuser);
1357 #else
1358     s = check_user_amandahosts(remotehost, pwptr, remoteuser);
1359 #endif
1360
1361     if (s != NULL) {
1362         *errstr = vstralloc("[",
1363                             "access as ", pwptr->pw_name, " not allowed",
1364                             " from ", remoteuser, "@", remotehost,
1365                             ": ", s, "]", NULL);
1366     }
1367     amfree(s);
1368     amfree(remotehost);
1369     amfree(remoteuser);
1370     return *errstr == NULL;
1371 }
1372
1373
1374 /*
1375  * Create the server end of a stream.  For bsd, this means setup a tcp
1376  * socket for receiving a connection.
1377  */
1378 static void *
1379 bsd_stream_server(h)
1380     void *h;
1381 {
1382     struct bsd_stream *bs = NULL;
1383 #ifndef TEST                                                    /* { */
1384     struct bsd_handle *bh = h;
1385
1386     assert(bh != NULL);
1387
1388     bs = alloc(sizeof(*bs));
1389     security_streaminit(&bs->secstr, &bsd_security_driver);
1390     bs->socket = stream_server(&bs->port, STREAM_BUFSIZE, STREAM_BUFSIZE);
1391     if (bs->socket < 0) {
1392         security_seterror(&bh->sech,
1393             "can't create server stream: %s", strerror(errno));
1394         amfree(bs);
1395         return (NULL);
1396     }
1397     bs->fd = -1;
1398     bs->ev_read = NULL;
1399 #endif /* !TEST */                                              /* } */
1400     return (bs);
1401 }
1402
1403 /*
1404  * Accepts a new connection on unconnected streams.  Assumes it is ok to
1405  * block on accept()
1406  */
1407 static int
1408 bsd_stream_accept(s)
1409     void *s;
1410 {
1411 #ifndef TEST                                                    /* { */
1412     struct bsd_stream *bs = s;
1413
1414     assert(bs != NULL);
1415     assert(bs->socket != -1);
1416     assert(bs->fd < 0);
1417
1418     bs->fd = stream_accept(bs->socket, 30, -1, -1);
1419     if (bs->fd < 0) {
1420         security_stream_seterror(&bs->secstr,
1421             "can't accept new stream connection: %s", strerror(errno));
1422         return (-1);
1423     }
1424 #endif /* !TEST */                                              /* } */
1425     return (0);
1426 }
1427
1428 /*
1429  * Return a connected stream
1430  */
1431 static void *
1432 bsd_stream_client(h, id)
1433     void *h;
1434     int id;
1435 {
1436     struct bsd_stream *bs = NULL;
1437 #ifndef TEST                                                    /* { */
1438     struct bsd_handle *bh = h;
1439 #ifdef DUMPER_SOCKET_BUFFERING
1440     int rcvbuf = sizeof(bs->databuf) * 2;
1441 #endif
1442
1443     assert(bh != NULL);
1444
1445     if (id < 0) {
1446         security_seterror(&bh->sech,
1447             "%d: invalid security stream id", id);
1448         return (NULL);
1449     }
1450
1451     bs = alloc(sizeof(*bs));
1452     security_streaminit(&bs->secstr, &bsd_security_driver);
1453     bs->fd = stream_client(bh->hostname, id, STREAM_BUFSIZE, STREAM_BUFSIZE,
1454         &bs->port, 0);
1455     if (bs->fd < 0) {
1456         security_seterror(&bh->sech,
1457             "can't connect stream to %s port %d: %s", bh->hostname,
1458             id, strerror(errno));
1459         amfree(bs);
1460         return (NULL);
1461     }
1462     bs->socket = -1;    /* we're a client */
1463     bs->ev_read = NULL;
1464 #ifdef DUMPER_SOCKET_BUFFERING
1465     setsockopt(bs->fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, sizeof(rcvbuf));
1466 #endif
1467 #endif /* !TEST */                                              /* } */
1468     return (bs);
1469 }
1470
1471 /*
1472  * Close and unallocate resources for a stream
1473  */
1474 static void
1475 bsd_stream_close(s)
1476     void *s;
1477 {
1478     struct bsd_stream *bs = s;
1479
1480     assert(bs != NULL);
1481
1482     if (bs->fd != -1)
1483         aclose(bs->fd);
1484     if (bs->socket != -1)
1485         aclose(bs->socket);
1486     bsd_stream_read_cancel(bs);
1487     amfree(bs);
1488 }
1489
1490 /*
1491  * Authenticate a stream.  bsd streams have no authentication
1492  */
1493 static int
1494 bsd_stream_auth(s)
1495     void *s;
1496 {
1497
1498     return (0); /* success */
1499 }
1500
1501 /*
1502  * Returns the stream id for this stream.  This is just the local port.
1503  */
1504 static int
1505 bsd_stream_id(s)
1506     void *s;
1507 {
1508     struct bsd_stream *bs = s;
1509
1510     assert(bs != NULL);
1511
1512     return (bs->port);
1513 }
1514
1515 /*
1516  * Write a chunk of data to a stream.  Blocks until completion.
1517  */
1518 static int
1519 bsd_stream_write(s, buf, size)
1520     void *s;
1521     const void *buf;
1522     size_t size;
1523 {
1524 #ifndef TEST                                                    /* { */
1525     struct bsd_stream *bs = s;
1526
1527     assert(bs != NULL);
1528
1529     if (fullwrite(bs->fd, buf, size) < 0) {
1530         security_stream_seterror(&bs->secstr,
1531             "write error on stream %d: %s", bs->port, strerror(errno));
1532         return (-1);
1533     }
1534 #endif /* !TEST */                                              /* } */
1535     return (0);
1536 }
1537
1538 /*
1539  * Submit a request to read some data.  Calls back with the given function
1540  * and arg when completed.
1541  */
1542 static void
1543 bsd_stream_read(s, fn, arg)
1544     void *s, *arg;
1545     void (*fn) P((void *, void *, ssize_t));
1546 {
1547     struct bsd_stream *bs = s;
1548
1549     /*
1550      * Only one read request can be active per stream.
1551      */
1552     if (bs->ev_read != NULL)
1553         event_release(bs->ev_read);
1554
1555     bs->ev_read = event_register(bs->fd, EV_READFD, stream_read_callback, bs);
1556     bs->fn = fn;
1557     bs->arg = arg;
1558 }
1559
1560 /*
1561  * Cancel a previous stream read request.  It's ok if we didn't
1562  * have a read scheduled.
1563  */
1564 static void
1565 bsd_stream_read_cancel(s)
1566     void *s;
1567 {
1568     struct bsd_stream *bs = s;
1569
1570     assert(bs != NULL);
1571
1572     if (bs->ev_read != NULL) {
1573         event_release(bs->ev_read);
1574         bs->ev_read = NULL;
1575     }
1576 }
1577
1578 /*
1579  * Callback for bsd_stream_read
1580  */
1581 static void
1582 stream_read_callback(arg)
1583     void *arg;
1584 {
1585     struct bsd_stream *bs = arg;
1586     ssize_t n;
1587
1588     assert(bs != NULL);
1589
1590     /*
1591      * Remove the event first, in case they reschedule it in the callback.
1592      */
1593     bsd_stream_read_cancel(bs);
1594     do {
1595         n = read(bs->fd, bs->databuf, sizeof(bs->databuf));
1596     } while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
1597     if (n < 0)
1598         security_stream_seterror(&bs->secstr, strerror(errno));
1599     (*bs->fn)(bs->arg, bs->databuf, n);
1600 }
1601
1602 /*
1603  * Convert a packet header into a string
1604  */
1605 static const char *
1606 pkthdr2str(bh, pkt)
1607     const struct bsd_handle *bh;
1608     const pkt_t *pkt;
1609 {
1610     static char retbuf[256];
1611
1612     assert(bh != NULL);
1613     assert(pkt != NULL);
1614
1615     snprintf(retbuf, sizeof(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n",
1616         VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type),
1617         bh->proto_handle, bh->sequence);
1618
1619     bsdprintf(("%s: pkthdr2str handle '%s'\n",
1620                debug_prefix_time(NULL), bh->proto_handle));
1621
1622     /* check for truncation.  If only we had asprintf()... */
1623     assert(retbuf[strlen(retbuf) - 1] == '\n');
1624
1625     return (retbuf);
1626 }
1627
1628 /*
1629  * Parses out the header line in 'str' into the pkt and handle
1630  * Returns negative on parse error.
1631  */
1632 static int
1633 str2pkthdr()
1634 {
1635     char *str;
1636     const char *tok;
1637     pkt_t *pkt;
1638
1639     pkt = &netfd.pkt;
1640
1641     assert(netfd.dgram.cur != NULL);
1642     str = stralloc(netfd.dgram.cur);
1643
1644     /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */
1645
1646     /* Read in "Amanda" */
1647     if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0)
1648         goto parse_error;
1649
1650     /* nothing is done with the major/minor numbers currently */
1651     if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL)
1652         goto parse_error;
1653
1654     /* Read in the packet type */
1655     if ((tok = strtok(NULL, " ")) == NULL)
1656         goto parse_error;
1657     pkt_init(pkt, pkt_str2type(tok), "");
1658     if (pkt->type == (pktype_t)-1)    
1659         goto parse_error;
1660
1661     /* Read in "HANDLE" */
1662     if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0)
1663         goto parse_error;
1664
1665     /* parse the handle */
1666     if ((tok = strtok(NULL, " ")) == NULL)
1667         goto parse_error;
1668     netfd.handle = stralloc(tok);
1669
1670     /* Read in "SEQ" */
1671     if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0)   
1672         goto parse_error;
1673
1674     /* parse the sequence number */   
1675     if ((tok = strtok(NULL, "\n")) == NULL)
1676         goto parse_error;
1677     netfd.sequence = atoi(tok);
1678
1679     /* Save the body, if any */       
1680     if ((tok = strtok(NULL, "")) != NULL)
1681         pkt_cat(pkt, "%s", tok);
1682
1683     amfree(str);
1684     return (0);
1685
1686 parse_error:
1687 #if 0 /* XXX we have no way of passing this back up */
1688     security_seterror(&bh->sech,
1689         "parse error in packet header : '%s'", origstr);
1690 #endif
1691     amfree(str);
1692     return (-1);
1693 }
1694
1695 #endif  /* BSD_SECURITY */                                      /* } */
1696
1697 #if defined(TEST)                                               /* { */
1698
1699 /*
1700  * The following dummy bind_portrange function is so we do not need to
1701  * drag in util.o just for the test program.
1702  */
1703 int
1704 bind_portrange(s, addrp, first_port, last_port, proto)
1705     int s;
1706     struct sockaddr_in *addrp;
1707     int first_port, last_port;
1708     char *proto;
1709 {
1710     return 0;
1711 }
1712
1713 /*
1714  * Construct a datestamp (YYYYMMDD) from a time_t.
1715  */
1716 char *
1717 construct_datestamp(t)
1718     time_t *t;
1719 {
1720     struct tm *tm;
1721     char datestamp[3*NUM_STR_SIZE];
1722     time_t when;
1723
1724     if(t == NULL) {
1725         when = time((time_t *)NULL);
1726     } else {
1727         when = *t;
1728     }
1729     tm = localtime(&when);
1730     snprintf(datestamp, sizeof(datestamp),
1731              "%04d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
1732     return stralloc(datestamp);
1733 }
1734
1735 /*
1736  * Construct a timestamp (YYYYMMDDHHMMSS) from a time_t.
1737  */
1738 char *
1739 construct_timestamp(t)
1740     time_t *t;
1741 {
1742     struct tm *tm;
1743     char timestamp[6*NUM_STR_SIZE];
1744     time_t when;
1745
1746     if(t == NULL) {
1747         when = time((time_t *)NULL);
1748     } else {
1749         when = *t;
1750     }
1751     tm = localtime(&when);
1752     snprintf(timestamp, sizeof(timestamp),
1753              "%04d%02d%02d%02d%02d%02d",
1754              tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
1755              tm->tm_hour, tm->tm_min, tm->tm_sec);
1756     return stralloc(timestamp);
1757 }
1758
1759 /*
1760  * The following are so we can include security.o but not all the rest
1761  * of the security modules.
1762  */
1763 const security_driver_t krb4_security_driver = {};
1764 const security_driver_t krb5_security_driver = {};
1765 const security_driver_t rsh_security_driver = {};
1766
1767 /*
1768  * This function will be called to accept the connection and is used
1769  * to report success or failure.
1770  */
1771 static void fake_accept_function(handle, pkt)
1772     security_handle_t *handle;
1773     pkt_t *pkt;
1774 {
1775     if (pkt == NULL) {
1776         fputs(handle->error, stdout);
1777         fputc('\n', stdout);
1778     } else {
1779         fputs("access is allowed\n", stdout);
1780     }
1781 }
1782
1783 int
1784 main (argc, argv)
1785 {
1786     char *remoteuser;
1787     char *remotehost;
1788     struct hostent *hp;
1789     struct bsd_handle *bh;
1790     void *save_cur;
1791     struct passwd *pwent;
1792
1793     /* Don't die when child closes pipe */
1794     signal(SIGPIPE, SIG_IGN);
1795
1796     /*
1797      * The following is stolen from amandad to emulate what it would
1798      * do on startup.
1799      */
1800     if(client_uid == (uid_t) -1 && (pwent = getpwnam(CLIENT_LOGIN)) != NULL) {
1801         client_uid = pwent->pw_uid;
1802         client_gid = pwent->pw_gid;
1803         endpwent();
1804     }
1805
1806 #ifdef FORCE_USERID
1807     /* we'd rather not run as root */
1808     if (geteuid() == 0) {
1809         if(client_uid == (uid_t) -1) {
1810             error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
1811         }
1812         initgroups(CLIENT_LOGIN, client_gid);
1813         setgid(client_gid);
1814         setegid(client_gid);
1815         seteuid(client_uid);
1816     }
1817 #endif  /* FORCE_USERID */
1818
1819     if (isatty(0)) {
1820         fputs("Remote user: ", stdout);
1821         fflush(stdout);
1822     }
1823     if ((remoteuser = agets(stdin)) == NULL) {
1824         return 0;
1825     }
1826
1827     if (isatty(0)) {
1828         fputs("Remote host: ", stdout);
1829         fflush(stdout);
1830     }
1831     if ((remotehost = agets(stdin)) == NULL) {
1832         return 0;
1833     }
1834
1835     set_pname("security");
1836     startclock();
1837
1838     if ((hp = gethostbyname(remotehost)) == NULL) {
1839         fprintf(stderr, "cannot look up remote host %s\n", remotehost);
1840         return 1;
1841     }
1842     memcpy((char *)&netfd.peer.sin_addr,
1843            (char *)hp->h_addr,
1844            sizeof(hp->h_addr));
1845     /*
1846      * Fake that it is coming from a reserved port.
1847      */
1848     netfd.peer.sin_port = htons(IPPORT_RESERVED - 1);
1849
1850     bh = alloc(sizeof(*bh));
1851     bh->proto_handle=NULL;
1852     netfd.pkt.type = P_REQ;
1853     dgram_zero(&netfd.dgram);
1854     save_cur = netfd.dgram.cur;                         /* cheating */
1855     dgram_cat(&netfd.dgram, "%s", pkthdr2str(bh, &netfd.pkt));
1856     dgram_cat(&netfd.dgram, "SECURITY USER %s\n", remoteuser);
1857     netfd.dgram.cur = save_cur;                         /* cheating */
1858
1859     accept_fn = fake_accept_function;
1860     netfd_read_callback(NULL);
1861
1862     return 0;
1863 }
1864
1865 #endif                                                          /* } */