Imported Upstream version 2.5.1
[debian/amanda] / common-src / security-util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1999 University of Maryland
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26
27 /*
28  * $Id: security-util.c,v 1.25 2006/07/22 12:04:47 martinea Exp $
29  *
30  * sec-security.c - security and transport over sec or a sec-like command.
31  *
32  * XXX still need to check for initial keyword on connect so we can skip
33  * over shell garbage and other stuff that sec might want to spew out.
34  */
35
36 #include "amanda.h"
37 #include "util.h"
38 #include "event.h"
39 #include "packet.h"
40 #include "queue.h"
41 #include "security.h"
42 #include "security-util.h"
43 #include "stream.h"
44 #include "version.h"
45
46 /* #define      SEC_DEBUG */
47 #define SHOW_SECURITY_DETAIL
48
49 #ifdef SEC_DEBUG
50 #  define       secprintf(x)    dbprintf(x)
51 #else
52 #  ifdef __lint
53 #    define     secprintf(x)    (void)(x)
54 #  else
55 #    define     secprintf(x)
56 #  endif
57 #endif
58
59 /*
60  * Magic values for sec_conn->handle
61  */
62 #define H_TAKEN -1              /* sec_conn->tok was already read */
63 #define H_EOF   -2              /* this connection has been shut down */
64
65 /*
66  * This is a queue of open connections
67  */
68 struct connq_s connq = {
69     TAILQ_HEAD_INITIALIZER(connq.tailq), 0
70 };
71 static int newhandle = 1;
72 static int newevent = 1;
73
74 /*
75  * Local functions
76  */
77 static void recvpkt_callback(void *, void *, ssize_t);
78 static void stream_read_callback(void *);
79 static void stream_read_sync_callback(void *);
80
81 static void sec_tcp_conn_read_cancel(struct tcp_conn *);
82 static void sec_tcp_conn_read_callback(void *);
83
84
85 /*
86  * Authenticate a stream
87  * Nothing needed for sec.  The connection is authenticated by secd
88  * on startup.
89  */
90 int
91 sec_stream_auth(
92     void *      s)
93 {
94     (void)s;    /* Quiet unused parameter warning */
95     return (0);
96 }
97
98 /*
99  * Returns the stream id for this stream.  This is just the local
100  * port.
101  */
102 int
103 sec_stream_id(
104     void *      s)
105 {
106     struct sec_stream *rs = s;
107
108     assert(rs != NULL);
109
110     return (rs->handle);
111 }
112
113 /*
114  * Setup to handle new incoming connections
115  */
116 void
117 sec_accept(
118     const security_driver_t *driver,
119     int         in,
120     int         out,
121     void        (*fn)(security_handle_t *, pkt_t *))
122 {
123     struct tcp_conn *rc;
124
125     rc = sec_tcp_conn_get("unknown",0);
126     rc->read = in;
127     rc->write = out;
128     rc->accept_fn = fn;
129     rc->driver = driver;
130     sec_tcp_conn_read(rc);
131 }
132
133 /*
134  * frees a handle allocated by the above
135  */
136 void
137 sec_close(
138     void *      inst)
139 {
140     struct sec_handle *rh = inst;
141
142     assert(rh != NULL);
143
144     secprintf(("%s: sec: closing handle to %s\n", debug_prefix_time(NULL),
145                rh->hostname));
146
147     if (rh->rs != NULL) {
148         /* This may be null if we get here on an error */
149         stream_recvpkt_cancel(rh);
150         security_stream_close(&rh->rs->secstr);
151     }
152     /* keep us from getting here again */
153     rh->sech.driver = NULL;
154     amfree(rh->hostname);
155     amfree(rh);
156 }
157
158 /*
159  * Called when a sec connection is finished connecting and is ready
160  * to be authenticated.
161  */
162 void
163 sec_connect_callback(
164     void *      cookie)
165 {
166     struct sec_handle *rh = cookie;
167
168     event_release(rh->rs->ev_read);
169     rh->rs->ev_read = NULL;
170     event_release(rh->ev_timeout);
171     rh->ev_timeout = NULL;
172
173     (*rh->fn.connect)(rh->arg, &rh->sech, S_OK);
174 }
175
176 /*
177  * Called if a connection times out before completion.
178  */
179 void
180 sec_connect_timeout(
181     void *      cookie)
182 {
183     struct sec_handle *rh = cookie;
184
185     event_release(rh->rs->ev_read);
186     rh->rs->ev_read = NULL;
187     event_release(rh->ev_timeout);
188     rh->ev_timeout = NULL;
189
190     (*rh->fn.connect)(rh->arg, &rh->sech, S_TIMEOUT);
191 }
192
193 void
194 sec_close_connection_none(
195     void *h,
196     char *hostname)
197 {
198     h = h;
199     hostname = hostname;
200
201     return;
202 }
203
204
205
206 /*
207  * Transmit a packet.
208  */
209 ssize_t
210 stream_sendpkt(
211     void *      cookie,
212     pkt_t *     pkt)
213 {
214     char *buf;
215     struct sec_handle *rh = cookie;
216     size_t len;
217     char *s;
218
219     assert(rh != NULL);
220     assert(pkt != NULL);
221
222     secprintf(("%s: sec: stream_sendpkt: enter\n", debug_prefix_time(NULL)));
223
224     if (rh->rc->prefix_packet)
225         s = rh->rc->prefix_packet(rh, pkt);
226     else
227         s = "";
228     len = strlen(pkt->body) + strlen(s) + 2;
229     buf = alloc(len);
230     buf[0] = (char)pkt->type;
231     strncpy(&buf[1], s, len - 1);
232     strncpy(&buf[1 + strlen(s)], pkt->body, (len - strlen(s) - 1));
233     if (strlen(s) > 0)
234         amfree(s);
235
236     secprintf((
237             "%s: sec: stream_sendpkt: %s (%d) pkt_t (len %d) contains:\n\n\"%s\"\n\n",
238             debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->type,
239             strlen(pkt->body), pkt->body));
240
241     if (security_stream_write(&rh->rs->secstr, buf, len) < 0) {
242         security_seterror(&rh->sech, security_stream_geterror(&rh->rs->secstr));
243         return (-1);
244     }
245     amfree(buf);
246     return (0);
247 }
248
249 /*
250  * Set up to receive a packet asyncronously, and call back when
251  * it has been read.
252  */
253 void
254 stream_recvpkt(
255     void *      cookie,
256     void        (*fn)(void *, pkt_t *, security_status_t),
257     void *      arg,
258     int         timeout)
259 {
260     struct sec_handle *rh = cookie;
261
262     assert(rh != NULL);
263
264     secprintf(("%s: sec: recvpkt registered for %s\n",
265                debug_prefix_time(NULL), rh->hostname));
266
267     /*
268      * Reset any pending timeout on this handle
269      */
270     if (rh->ev_timeout != NULL)
271         event_release(rh->ev_timeout);
272
273     /*
274      * Negative timeouts mean no timeout
275      */
276     if (timeout < 0) {
277         rh->ev_timeout = NULL;
278     } else {
279         rh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
280                 stream_recvpkt_timeout, rh);
281     }
282     rh->fn.recvpkt = fn;
283     rh->arg = arg;
284     security_stream_read(&rh->rs->secstr, recvpkt_callback, rh);
285 }
286
287 /*
288  * This is called when a handle times out before receiving a packet.
289  */
290 void
291 stream_recvpkt_timeout(
292     void *      cookie)
293 {
294     struct sec_handle *rh = cookie;
295
296     assert(rh != NULL);
297
298     secprintf(("%s: sec: recvpkt timeout for %s\n",
299                debug_prefix_time(NULL), rh->hostname));
300
301     stream_recvpkt_cancel(rh);
302     (*rh->fn.recvpkt)(rh->arg, NULL, S_TIMEOUT);
303 }
304
305 /*
306  * Remove a async receive request from the queue
307  */
308 void
309 stream_recvpkt_cancel(
310     void *      cookie)
311 {
312     struct sec_handle *rh = cookie;
313
314     secprintf(("%s: sec: cancelling recvpkt for %s\n",
315                debug_prefix_time(NULL), rh->hostname));
316
317     assert(rh != NULL);
318
319     security_stream_read_cancel(&rh->rs->secstr);
320     if (rh->ev_timeout != NULL) {
321         event_release(rh->ev_timeout);
322         rh->ev_timeout = NULL;
323     }
324 }
325
326 /*
327  * Write a chunk of data to a stream.  Blocks until completion.
328  */
329 int
330 tcpm_stream_write(
331     void *      s,
332     const void *buf,
333     size_t      size)
334 {
335     struct sec_stream *rs = s;
336
337     assert(rs != NULL);
338     assert(rs->rc != NULL);
339
340     secprintf(("%s: sec: stream_write: writing %d bytes to %s:%d %d\n",
341                debug_prefix_time(NULL), size, rs->rc->hostname, rs->handle,
342                rs->rc->write));
343
344     if (tcpm_send_token(rs->rc->write, rs->handle, &rs->rc->errmsg,
345                              buf, size)) {
346         security_stream_seterror(&rs->secstr, rs->rc->errmsg);
347         return (-1);
348     }
349     return (0);
350 }
351
352 /*
353  * Submit a request to read some data.  Calls back with the given
354  * function and arg when completed.
355  */
356 void
357 tcpm_stream_read(
358     void *      s,
359     void        (*fn)(void *, void *, ssize_t),
360     void *      arg)
361 {
362     struct sec_stream *rs = s;
363
364     assert(rs != NULL);
365
366     /*
367      * Only one read request can be active per stream.
368      */
369     if (rs->ev_read == NULL) {
370         rs->ev_read = event_register((event_id_t)rs->rc, EV_WAIT,
371             stream_read_callback, rs);
372         sec_tcp_conn_read(rs->rc);
373     }
374     rs->fn = fn;
375     rs->arg = arg;
376 }
377
378 /*
379  * Write a chunk of data to a stream.  Blocks until completion.
380  */
381 ssize_t
382 tcpm_stream_read_sync(
383     void *      s,
384     void **     buf)
385 {
386     struct sec_stream *rs = s;
387
388     assert(rs != NULL);
389
390     /*
391      * Only one read request can be active per stream.
392      */
393     if (rs->ev_read != NULL) {
394         return -1;
395     }
396     rs->ev_read = event_register((event_id_t)rs->rc, EV_WAIT,
397         stream_read_sync_callback, rs);
398     sec_tcp_conn_read(rs->rc);
399     event_wait(rs->ev_read);
400     *buf = rs->rc->pkt;
401     return (rs->rc->pktlen);
402 }
403
404 /*
405  * Cancel a previous stream read request.  It's ok if we didn't have a read
406  * scheduled.
407  */
408 void
409 tcpm_stream_read_cancel(
410     void *      s)
411 {
412     struct sec_stream *rs = s;
413
414     assert(rs != NULL);
415
416     if (rs->ev_read != NULL) {
417         event_release(rs->ev_read);
418         rs->ev_read = NULL;
419         sec_tcp_conn_read_cancel(rs->rc);
420     }
421 }
422
423 /*
424  * Transmits a chunk of data over a rsh_handle, adding
425  * the necessary headers to allow the remote end to decode it.
426  */
427 ssize_t
428 tcpm_send_token(
429     int         fd,
430     int         handle,
431     char **     errmsg,
432     const void *buf,
433     size_t      len)
434 {
435     uint32_t            nethandle;
436     uint32_t            netlength;
437     struct iovec        iov[3];
438     int                 nb_iov = 3;
439
440     assert(SIZEOF(netlength) == 4);
441
442     /*
443      * Format is:
444      *   32 bit length (network byte order)
445      *   32 bit handle (network byte order)
446      *   data
447      */
448     netlength = htonl(len);
449     iov[0].iov_base = (void *)&netlength;
450     iov[0].iov_len = SIZEOF(netlength);
451
452     nethandle = htonl((uint32_t)handle);
453     iov[1].iov_base = (void *)&nethandle;
454     iov[1].iov_len = SIZEOF(nethandle);
455
456     if(len == 0) {
457         nb_iov = 2;
458     }
459     else {
460         iov[2].iov_base = (void *)buf;
461         iov[2].iov_len = len;
462         nb_iov = 3;
463     }
464
465     if (net_writev(fd, iov, nb_iov) < 0) {
466         if (errmsg)
467             *errmsg = newvstralloc(*errmsg, "write error to ",
468                                    ": ", strerror(errno), NULL);
469         return (-1);
470     }
471     return (0);
472 }
473
474 /*
475  *  return -1 on error
476  *  return  0 on EOF:   *handle = H_EOF  && *size = 0    if socket closed
477  *  return  0 on EOF:   *handle = handle && *size = 0    if stream closed
478  *  return size     :   *handle = handle && *size = size for data read
479  */
480
481 ssize_t
482 tcpm_recv_token(
483     int         fd,
484     int *       handle,
485     char **     errmsg,
486     char **     buf,
487     ssize_t *   size,
488     int         timeout)
489 {
490     unsigned int netint[2];
491
492     assert(SIZEOF(netint) == 8);
493
494     switch (net_read(fd, &netint, SIZEOF(netint), timeout)) {
495     case -1:
496         if (errmsg)
497             *errmsg = newvstralloc(*errmsg, "recv error: ", strerror(errno),
498                                    NULL);
499         secprintf(("%s: tcpm_recv_token: A return(-1)\n",
500                    debug_prefix_time(NULL)));
501         return (-1);
502     case 0:
503         *size = 0;
504         *handle = H_EOF;
505         *errmsg = newvstralloc(*errmsg, "SOCKET_EOF", NULL);
506         secprintf(("%s: tcpm_recv_token: A return(0)\n",
507                    debug_prefix_time(NULL)));
508         return (0);
509     default:
510         break;
511     }
512
513     *size = (ssize_t)ntohl(netint[0]);
514     if (*size > NETWORK_BLOCK_BYTES) {
515         *errmsg = newvstralloc(*errmsg, "tcpm_recv_token: invalid size",
516                                    NULL);
517         dbprintf(("%s: tcpm_recv_token: invalid size %d\n",
518                    debug_prefix_time(NULL), *size));
519         *size = -1;
520         return -1;
521     }
522     amfree(*buf);
523     *buf = alloc((size_t)*size);
524     *handle = (int)ntohl(netint[1]);
525
526     if(*size == 0) {
527         secprintf(("%s: tcpm_recv_token: read EOF from %d\n",
528                    debug_prefix_time(NULL), *handle));
529         *errmsg = newvstralloc(*errmsg, "EOF",
530                                    NULL);
531         return 0;
532     }
533     switch (net_read(fd, *buf, (size_t)*size, timeout)) {
534     case -1:
535         if (errmsg)
536             *errmsg = newvstralloc(*errmsg, "recv error: ", strerror(errno),
537                                    NULL);
538         secprintf(("%s: tcpm_recv_token: B return(-1)\n",
539                    debug_prefix_time(NULL)));
540         return (-1);
541     case 0:
542         *size = 0;
543         *errmsg = newvstralloc(*errmsg, "SOCKET_EOF", NULL);
544         secprintf(("%s: tcpm_recv_token: B return(0)\n",
545                    debug_prefix_time(NULL)));
546         return (0);
547     default:
548         break;
549     }
550
551     secprintf(("%s: tcpm_recv_token: read %ld bytes from %d\n",
552                debug_prefix_time(NULL), *size, *handle));
553     return((*size));
554 }
555
556 void
557 tcpm_close_connection(
558     void *h,
559     char *hostname)
560 {
561     struct sec_handle *rh = h;
562
563     hostname = hostname;
564
565     if(rh->rc->toclose == 0) {
566         rh->rc->toclose = 1;
567         sec_tcp_conn_put(rh->rc);
568     }
569 }
570
571
572
573 /*
574  * Accept an incoming connection on a stream_server socket
575  * Nothing needed for tcpma.
576  */
577 int
578 tcpma_stream_accept(
579     void *      s)
580 {
581     (void)s;    /* Quiet unused parameter warning */
582
583     return (0);
584 }
585
586 /*
587  * Return a connected stream.  For sec, this means setup a stream
588  * with the supplied handle.
589  */
590 void *
591 tcpma_stream_client(
592     void *      h,
593     int         id)
594 {
595     struct sec_handle *rh = h;
596     struct sec_stream *rs;
597
598     assert(rh != NULL);
599
600     if (id <= 0) {
601         security_seterror(&rh->sech,
602             "%hd: invalid security stream id", id);
603         return (NULL);
604     }
605
606     rs = alloc(SIZEOF(*rs));
607     security_streaminit(&rs->secstr, rh->sech.driver);
608     rs->handle = id;
609     rs->ev_read = NULL;
610     rs->closed_by_me = 0;
611     rs->closed_by_network = 0;
612     if (rh->rc) {
613         rs->rc = rh->rc;
614         rh->rc->refcnt++;
615     }
616     else {
617         rs->rc = sec_tcp_conn_get(rh->hostname, 0);
618         rs->rc->driver = rh->sech.driver;
619         rh->rc = rs->rc;
620     }
621
622     secprintf(("%s: sec: stream_client: connected to stream %hd\n",
623                debug_prefix_time(NULL), id));
624
625     return (rs);
626 }
627
628 /*
629  * Create the server end of a stream.  For sec, this means setup a stream
630  * object and allocate a new handle for it.
631  */
632 void *
633 tcpma_stream_server(
634     void *      h)
635 {
636     struct sec_handle *rh = h;
637     struct sec_stream *rs;
638
639     assert(rh != NULL);
640
641     rs = alloc(SIZEOF(*rs));
642     security_streaminit(&rs->secstr, rh->sech.driver);
643     rs->closed_by_me = 0;
644     rs->closed_by_network = 0;
645     if (rh->rc) {
646         rs->rc = rh->rc;
647         rs->rc->refcnt++;
648     }
649     else {
650         rs->rc = sec_tcp_conn_get(rh->hostname, 0);
651         rs->rc->driver = rh->sech.driver;
652         rh->rc = rs->rc;
653     }
654     /*
655      * Stream should already be setup!
656      */
657     if (rs->rc->read < 0) {
658         sec_tcp_conn_put(rs->rc);
659         amfree(rs);
660         security_seterror(&rh->sech, "lost connection to %s", rh->hostname);
661         return (NULL);
662     }
663     assert(strcmp(rh->hostname, rs->rc->hostname) == 0);
664     //amfree(rh->hostname);
665     //rh->hostname = stralloc(rs->rc->hostname);
666     /*
667      * so as not to conflict with the amanda server's handle numbers,
668      * we start at 500000 and work down
669      */
670     rs->handle = 500000 - newhandle++;
671     rs->ev_read = NULL;
672     secprintf(("%s: sec: stream_server: created stream %d\n",
673                debug_prefix_time(NULL), rs->handle));
674     return (rs);
675 }
676
677 /*
678  * Close and unallocate resources for a stream.
679  */
680 void
681 tcpma_stream_close(
682     void *      s)
683 {
684     struct sec_stream *rs = s;
685     char buf = 0;
686
687     assert(rs != NULL);
688
689     secprintf(("%s: sec: tcpma_stream_close: closing stream %d\n",
690                debug_prefix_time(NULL), rs->handle));
691
692     if(rs->closed_by_network == 0 && rs->rc->write != -1)
693         tcpm_stream_write(rs, &buf, 0);
694     security_stream_read_cancel(&rs->secstr);
695     if(rs->closed_by_network == 0)
696         sec_tcp_conn_put(rs->rc);
697     amfree(rs);
698 }
699
700 /*
701  * Create the server end of a stream.  For bsdudp, this means setup a tcp
702  * socket for receiving a connection.
703  */
704 void *
705 tcp1_stream_server(
706     void *      h)
707 {
708     struct sec_stream *rs = NULL;
709     struct sec_handle *rh = h;
710
711     assert(rh != NULL);
712
713     rs = alloc(SIZEOF(*rs));
714     security_streaminit(&rs->secstr, rh->sech.driver);
715     rs->closed_by_me = 0;
716     rs->closed_by_network = 0;
717     if (rh->rc) {
718         rs->rc = rh->rc;
719         rs->handle = 500000 - newhandle++;
720         rs->rc->refcnt++;
721         rs->socket = 0;         /* the socket is already opened */
722     }
723     else {
724         rh->rc = sec_tcp_conn_get(rh->hostname, 1);
725         rh->rc->driver = rh->sech.driver;
726         rs->rc = rh->rc;
727         rs->socket = stream_server(&rs->port, STREAM_BUFSIZE, 
728                 STREAM_BUFSIZE, 0);
729         if (rs->socket < 0) {
730             security_seterror(&rh->sech,
731                             "can't create server stream: %s", strerror(errno));
732             amfree(rs);
733             return (NULL);
734         }
735         rh->rc->read = rs->socket;
736         rh->rc->write = rs->socket;
737         rs->handle = (int)rs->port;
738     }
739     rs->fd = -1;
740     rs->ev_read = NULL;
741     return (rs);
742 }
743
744 /*
745  * Accepts a new connection on unconnected streams.  Assumes it is ok to
746  * block on accept()
747  */
748 int
749 tcp1_stream_accept(
750     void *      s)
751 {
752     struct sec_stream *bs = s;
753
754     assert(bs != NULL);
755     assert(bs->socket != -1);
756     assert(bs->fd < 0);
757
758     if (bs->socket > 0) {
759         bs->fd = stream_accept(bs->socket, 30, STREAM_BUFSIZE, STREAM_BUFSIZE);
760         if (bs->fd < 0) {
761             security_stream_seterror(&bs->secstr,
762                                      "can't accept new stream connection: %s",
763                                      strerror(errno));
764             return (-1);
765         }
766         bs->rc->read = bs->fd;
767         bs->rc->write = bs->fd;
768     }
769     return (0);
770 }
771
772 /*
773  * Return a connected stream
774  */
775 void *
776 tcp1_stream_client(
777     void *      h,
778     int         id)
779 {
780     struct sec_stream *rs = NULL;
781     struct sec_handle *rh = h;
782
783     assert(rh != NULL);
784
785     rs = alloc(SIZEOF(*rs));
786     security_streaminit(&rs->secstr, rh->sech.driver);
787     rs->handle = id;
788     rs->ev_read = NULL;
789     rs->closed_by_me = 0;
790     rs->closed_by_network = 0;
791     if (rh->rc) {
792         rs->rc = rh->rc;
793         rh->rc->refcnt++;
794     }
795     else {
796         rh->rc = sec_tcp_conn_get(rh->hostname, 1);
797         rs->rc = rh->rc;
798         rh->rc->read = stream_client(rh->hostname, (in_port_t)id,
799                         STREAM_BUFSIZE, STREAM_BUFSIZE, &rs->port, 0);
800         if (rh->rc->read < 0) {
801             security_seterror(&rh->sech,
802                               "can't connect stream to %s port %d: %s",
803                                rh->hostname, id, strerror(errno));
804             amfree(rs);
805             return (NULL);
806         }
807         rh->rc->write = rh->rc->read;
808     }
809     rs->socket = -1;    /* we're a client */
810     rh->rs = rs;
811     return (rs);
812 }
813
814 int
815 tcp_stream_write(
816     void *      s,
817     const void *buf,
818     size_t      size)
819 {
820     struct sec_stream *rs = s;
821
822     assert(rs != NULL);
823
824     if (fullwrite(rs->fd, buf, size) < 0) {
825         security_stream_seterror(&rs->secstr,
826             "write error on stream %d: %s", rs->port, strerror(errno));
827         return (-1);
828     }
829     return (0);
830 }
831
832 char *
833 bsd_prefix_packet(
834     void *      h,
835     pkt_t *     pkt)
836 {
837     struct sec_handle *rh = h;
838     struct passwd *pwd;
839     char *buf;
840
841     if (pkt->type != P_REQ)
842         return "";
843
844     if ((pwd = getpwuid(getuid())) == NULL) {
845         security_seterror(&rh->sech,
846                           "can't get login name for my uid %ld",
847                           (long)getuid());
848         return(NULL);
849     }
850     buf = alloc(16+strlen(pwd->pw_name));
851     strncpy(buf, "SECURITY USER ", (16 + strlen(pwd->pw_name)));
852     strncpy(&buf[14], pwd->pw_name, (16 + strlen(pwd->pw_name) - 14));
853     buf[14 + strlen(pwd->pw_name)] = '\n';
854     buf[15 + strlen(pwd->pw_name)] = '\0';
855
856     return (buf);
857 }
858
859
860 /*
861  * Check the security of a received packet.  Returns negative on security
862  * violation, or returns 0 if ok.  Removes the security info from the pkt_t.
863  */
864 int
865 bsd_recv_security_ok(
866     struct sec_handle * rh,
867     pkt_t *             pkt)
868 {
869     char *tok, *security, *body, *result;
870     char *service = NULL, *serviceX, *serviceY;
871     char *security_line;
872     size_t len;
873
874     /*
875      * Now, find the SECURITY line in the body, and parse it out
876      * into an argv.
877      */
878     if (strncmp(pkt->body, "SECURITY ", SIZEOF("SECURITY ") - 1) == 0) {
879         security = pkt->body;
880         len = 0;
881         while(*security != '\n' && len < pkt->size) {
882             security++;
883             len++;
884         }
885         if(*security == '\n') {
886             body = security+1;
887             *security = '\0';
888             security_line = stralloc(pkt->body);
889             security = pkt->body + strlen("SECURITY ");
890         } else {
891             body = pkt->body;
892             security_line = NULL;
893             security = NULL;
894         }
895     } else {
896         body = pkt->body;
897         security_line = NULL;
898         security = NULL;
899     }
900
901     /*
902      * Now, find the SERVICE line in the body, and parse it out
903      * into an argv.
904      */
905     if (strncmp(body, "SERVICE", SIZEOF("SERVICE") - 1) == 0) {
906         serviceX = stralloc(body + strlen("SERVICE "));
907         serviceY = strtok(serviceX, "\n");
908         if (serviceY)
909             service  = stralloc(serviceY);
910         amfree(serviceX);
911     }
912
913     /*
914      * We need to do different things depending on which type of packet
915      * this is.
916      */
917     switch (pkt->type) {
918     case P_REQ:
919         /*
920          * Request packets must come from a reserved port
921          */
922         if (ntohs(rh->peer.sin_port) >= IPPORT_RESERVED) {
923             security_seterror(&rh->sech,
924                 "host %s: port %d not secure", rh->hostname,
925                 ntohs(rh->peer.sin_port));
926             amfree(service);
927             amfree(security_line);
928             return (-1);
929         }
930
931         if (!service) {
932             security_seterror(&rh->sech,
933                               "packet as no SERVICE line");
934             amfree(security_line);
935             return (-1);
936         }
937
938         /*
939          * Request packets contain a remote username.  We need to check
940          * that we allow it in.
941          *
942          * They will look like:
943          *      SECURITY USER [username]
944          */
945
946         /* there must be some security info */
947         if (security == NULL) {
948             security_seterror(&rh->sech,
949                 "no bsd SECURITY for P_REQ");
950             amfree(service);
951             return (-1);
952         }
953
954         /* second word must be USER */
955         if ((tok = strtok(security, " ")) == NULL) {
956             security_seterror(&rh->sech,
957                 "SECURITY line: %s", security_line);
958             amfree(service);
959             amfree(security_line);
960             return (-1);        /* default errmsg */
961         }
962         if (strcmp(tok, "USER") != 0) {
963             security_seterror(&rh->sech,
964                 "REQ SECURITY line parse error, expecting USER, got %s", tok);
965             amfree(service);
966             amfree(security_line);
967             return (-1);
968         }
969
970         /* the third word is the username */
971         if ((tok = strtok(NULL, "")) == NULL) {
972             security_seterror(&rh->sech,
973                 "SECURITY line: %s", security_line);
974             amfree(security_line);
975             return (-1);        /* default errmsg */
976         }
977         if ((result = check_user(rh, tok, service)) != NULL) {
978             security_seterror(&rh->sech, "%s", result);
979             amfree(service);
980             amfree(result);
981             amfree(security_line);
982             return (-1);
983         }
984
985         /* we're good to go */
986         break;
987     default:
988         break;
989     }
990     amfree(service);
991     amfree(security_line);
992
993     /*
994      * If there is security info at the front of the packet, we need to
995      * shift the rest of the data up and nuke it.
996      */
997     if (body != pkt->body)
998         memmove(pkt->body, body, strlen(body) + 1);
999     return (0);
1000 }
1001
1002 /*
1003  * Transmit a packet.  Add security information first.
1004  */
1005 ssize_t
1006 udpbsd_sendpkt(
1007     void *      cookie,
1008     pkt_t *     pkt)
1009 {
1010     struct sec_handle *rh = cookie;
1011     struct passwd *pwd;
1012
1013     assert(rh != NULL);
1014     assert(pkt != NULL);
1015
1016     secprintf(("%s: udpbsd_sendpkt: enter\n", get_pname()));
1017     /*
1018      * Initialize this datagram, and add the header
1019      */
1020     dgram_zero(&rh->udp->dgram);
1021     dgram_cat(&rh->udp->dgram, pkthdr2str(rh, pkt));
1022
1023     /*
1024      * Add the security info.  This depends on which kind of packet we're
1025      * sending.
1026      */
1027     switch (pkt->type) {
1028     case P_REQ:
1029         /*
1030          * Requests get sent with our username in the body
1031          */
1032         if ((pwd = getpwuid(geteuid())) == NULL) {
1033             security_seterror(&rh->sech,
1034                 "can't get login name for my uid %ld", (long)getuid());
1035             return (-1);
1036         }
1037         dgram_cat(&rh->udp->dgram, "SECURITY USER %s\n", pwd->pw_name);
1038         break;
1039
1040     default:
1041         break;
1042     }
1043
1044     /*
1045      * Add the body, and send it
1046      */
1047     dgram_cat(&rh->udp->dgram, pkt->body);
1048
1049     secprintf((
1050             "%s: sec: udpbsd_sendpkt: %s (%d) pkt_t (len %d) contains:\n\n\"%s\"\n\n",
1051             debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->type,
1052             strlen(pkt->body), pkt->body));
1053
1054     if (dgram_send_addr(rh->peer, &rh->udp->dgram) != 0) {
1055         security_seterror(&rh->sech,
1056             "send %s to %s failed: %s", pkt_type2str(pkt->type),
1057             rh->hostname, strerror(errno));
1058         return (-1);
1059     }
1060     return (0);
1061 }
1062
1063 void
1064 udp_close(
1065     void *      cookie)
1066 {
1067     struct sec_handle *rh = cookie;
1068
1069     if (rh->proto_handle == NULL) {
1070         return;
1071     }
1072
1073     secprintf(("%s: udp: close handle '%s'\n",
1074                debug_prefix_time(NULL), rh->proto_handle));
1075
1076     udp_recvpkt_cancel(rh);
1077     if (rh->next) {
1078         rh->next->prev = rh->prev;
1079     }
1080     else {
1081         rh->udp->bh_last = rh->prev;
1082     }
1083     if (rh->prev) {
1084         rh->prev->next = rh->next;
1085     }
1086     else {
1087         rh->udp->bh_first = rh->next;
1088     }
1089
1090     amfree(rh->proto_handle);
1091     amfree(rh->hostname);
1092     amfree(rh);
1093 }
1094
1095 /*
1096  * Set up to receive a packet asynchronously, and call back when it has
1097  * been read.
1098  */
1099 void
1100 udp_recvpkt(
1101     void *      cookie,
1102     void        (*fn)(void *, pkt_t *, security_status_t),
1103     void *      arg,
1104     int         timeout)
1105 {
1106     struct sec_handle *rh = cookie;
1107
1108     secprintf(("%s: udp_recvpkt(cookie=%p, fn=%p, arg=%p, timeout=%u)\n",
1109         debug_prefix(NULL), cookie, fn, arg, timeout));
1110     assert(rh != NULL);
1111     assert(fn != NULL);
1112
1113
1114     /*
1115      * Subsequent recvpkt calls override previous ones
1116      */
1117     if (rh->ev_read == NULL) {
1118         udp_addref(rh->udp, &udp_netfd_read_callback);
1119         rh->ev_read = event_register(rh->event_id, EV_WAIT,
1120             udp_recvpkt_callback, rh);
1121     }
1122     if (rh->ev_timeout != NULL)
1123         event_release(rh->ev_timeout);
1124     if (timeout < 0)
1125         rh->ev_timeout = NULL;
1126     else
1127         rh->ev_timeout = event_register((event_id_t)timeout, EV_TIME,
1128                                         udp_recvpkt_timeout, rh);
1129     rh->fn.recvpkt = fn;
1130     rh->arg = arg;
1131 }
1132
1133 /*
1134  * Remove a async receive request on this handle from the queue.
1135  * If it is the last one to be removed, then remove the event
1136  * handler for our network fd
1137  */
1138 void
1139 udp_recvpkt_cancel(
1140     void *      cookie)
1141 {
1142     struct sec_handle *rh = cookie;
1143
1144     assert(rh != NULL);
1145
1146     if (rh->ev_read != NULL) {
1147         udp_delref(rh->udp);
1148         event_release(rh->ev_read);
1149         rh->ev_read = NULL;
1150     }
1151
1152     if (rh->ev_timeout != NULL) {
1153         event_release(rh->ev_timeout);
1154         rh->ev_timeout = NULL;
1155     }
1156 }
1157
1158 /*
1159  * This is called when a handle is woken up because data read off of the
1160  * net is for it.
1161  */
1162 void
1163 udp_recvpkt_callback(
1164     void *      cookie)
1165 {
1166     struct sec_handle *rh = cookie;
1167     void (*fn)(void *, pkt_t *, security_status_t);
1168     void *arg;
1169
1170     secprintf(("%s: udp: receive handle '%s' netfd '%s'\n",
1171                debug_prefix_time(NULL), rh->proto_handle, rh->udp->handle));
1172     assert(rh != NULL);
1173
1174     if (strcmp(rh->proto_handle, rh->udp->handle) != 0) assert(1);
1175     /* if it didn't come from the same host/port, forget it */
1176     if (memcmp(&rh->peer.sin_addr, &rh->udp->peer.sin_addr,
1177         SIZEOF(rh->udp->peer.sin_addr)) != 0 ||
1178         rh->peer.sin_port != rh->udp->peer.sin_port) {
1179         amfree(rh->udp->handle);
1180         //rh->udp->handle = NULL;
1181         return;
1182     }
1183
1184     /*
1185      * We need to cancel the recvpkt request before calling the callback
1186      * because the callback may reschedule us.
1187      */
1188     fn = rh->fn.recvpkt;
1189     arg = rh->arg;
1190     udp_recvpkt_cancel(rh);
1191
1192     /*
1193      * Check the security of the packet.  If it is bad, then pass NULL
1194      * to the packet handling function instead of a packet.
1195      */
1196     if (rh->udp->recv_security_ok &&
1197         rh->udp->recv_security_ok(rh, &rh->udp->pkt) < 0)
1198         (*fn)(arg, NULL, S_ERROR);
1199     else
1200         (*fn)(arg, &rh->udp->pkt, S_OK);
1201 }
1202
1203 /*
1204  * This is called when a handle times out before receiving a packet.
1205  */
1206 void
1207 udp_recvpkt_timeout(
1208     void *      cookie)
1209 {
1210     struct sec_handle *rh = cookie;
1211     void (*fn)(void *, pkt_t *, security_status_t);
1212     void *arg;
1213
1214     assert(rh != NULL);
1215
1216     assert(rh->ev_timeout != NULL);
1217     fn = rh->fn.recvpkt;
1218     arg = rh->arg;
1219     udp_recvpkt_cancel(rh);
1220     (*fn)(arg, NULL, S_TIMEOUT);
1221 }
1222
1223 /*
1224  * Given a hostname and a port, setup a udp_handle
1225  */
1226 int
1227 udp_inithandle(
1228     udp_handle_t *      udp,
1229     struct sec_handle * rh,
1230     struct hostent *    he,
1231     in_port_t           port,
1232     char *              handle,
1233     int                 sequence)
1234 {
1235     int i;
1236
1237     /*
1238      * Save the hostname and port info
1239      */
1240     secprintf(("%s: udp_inithandle port %hd handle %s sequence %d\n",
1241                debug_prefix_time(NULL), ntohs(port),
1242                handle, sequence));
1243     assert(he != NULL);
1244
1245     rh->hostname = stralloc(he->h_name);
1246     memcpy(&rh->peer.sin_addr, he->h_addr, SIZEOF(rh->peer.sin_addr));
1247     rh->peer.sin_port = port;
1248     rh->peer.sin_family = (sa_family_t)AF_INET;
1249
1250     /*
1251      * Do a forward lookup of the hostname.  This is unnecessary if we
1252      * are initiating the connection, but is very serious if we are
1253      * receiving.  We want to make sure the hostname
1254      * resolves back to the remote ip for security reasons.
1255      */
1256     if ((he = gethostbyname(rh->hostname)) == NULL) {
1257     secprintf(("%s: udp: bb\n", debug_prefix_time(NULL)));
1258         security_seterror(&rh->sech,
1259             "%s: could not resolve hostname", rh->hostname);
1260         return (-1);
1261     }
1262
1263     /*
1264      * Make sure the hostname matches.  This should always work.
1265      */
1266     if (strncasecmp(rh->hostname, he->h_name, strlen(rh->hostname)) != 0) {
1267     secprintf(("%s: udp: cc\n", debug_prefix_time(NULL)));
1268         security_seterror(&rh->sech,
1269                           "%s: did not resolve to itself, it resolv to",
1270                           rh->hostname, he->h_name);
1271         return (-1);
1272     }
1273
1274     /*
1275      * Now look for a matching ip address.
1276      */
1277     for (i = 0; he->h_addr_list[i] != NULL; i++) {
1278         if (memcmp(&rh->peer.sin_addr, he->h_addr_list[i],
1279             SIZEOF(struct in_addr)) == 0) {
1280             break;
1281         }
1282     }
1283
1284     /*
1285      * If we didn't find it, try the aliases.  This is a workaround for
1286      * Solaris if DNS goes over NIS.
1287      */
1288     if (he->h_addr_list[i] == NULL) {
1289         const char *ipstr = inet_ntoa(rh->peer.sin_addr);
1290         for (i = 0; he->h_aliases[i] != NULL; i++) {
1291             if (strcmp(he->h_aliases[i], ipstr) == 0)
1292                 break;
1293         }
1294         /*
1295          * No aliases either.  Failure.  Someone is fooling with us or
1296          * DNS is messed up.
1297          */
1298         if (he->h_aliases[i] == NULL) {
1299             security_seterror(&rh->sech,
1300                 "DNS check failed: no matching ip address for %s",
1301                 rh->hostname);
1302             return (-1);
1303         }
1304     }
1305
1306     rh->prev = udp->bh_last;
1307     if (udp->bh_last) {
1308         rh->prev->next = rh;
1309     }
1310     if (!udp->bh_first) {
1311         udp->bh_first = rh;
1312     }
1313     rh->next = NULL;
1314     udp->bh_last = rh;
1315
1316     rh->sequence = sequence;
1317     rh->event_id = (event_id_t)newevent++;
1318     amfree(rh->proto_handle);
1319     rh->proto_handle = stralloc(handle);
1320     rh->fn.connect = NULL;
1321     rh->arg = NULL;
1322     rh->ev_read = NULL;
1323     rh->ev_timeout = NULL;
1324
1325     secprintf(("%s: udp: adding handle '%s'\n",
1326                debug_prefix_time(NULL), rh->proto_handle));
1327
1328     return(0);
1329 }
1330
1331
1332 /*
1333  * Callback for received packets.  This is the function bsd_recvpkt
1334  * registers with the event handler.  It is called when the event handler
1335  * realizes that data is waiting to be read on the network socket.
1336  */
1337 void
1338 udp_netfd_read_callback(
1339     void *      cookie)
1340 {
1341     struct udp_handle *udp = cookie;
1342     struct sec_handle *rh;
1343     struct hostent *he;
1344     int a;
1345
1346     secprintf(("%s: udp_netfd_read_callback(cookie=%p)\n",
1347                 debug_prefix(NULL), cookie));
1348     assert(udp != NULL);
1349     
1350 #ifndef TEST                                                    /* { */
1351     /*
1352      * Receive the packet.
1353      */
1354     dgram_zero(&udp->dgram);
1355     if (dgram_recv(&udp->dgram, 0, &udp->peer) < 0)
1356         return;
1357 #endif /* !TEST */                                              /* } */
1358
1359     /*
1360      * Parse the packet.
1361      */
1362     if (str2pkthdr(udp) < 0)
1363         return;
1364
1365     /*
1366      * If there are events waiting on this handle, we're done
1367      */
1368     rh = udp->bh_first;
1369     while(rh != NULL && (strcmp(rh->proto_handle, udp->handle) != 0 ||
1370                          rh->sequence != udp->sequence ||
1371                          rh->peer.sin_addr.s_addr != udp->peer.sin_addr.s_addr ||
1372                          rh->peer.sin_port != udp->peer.sin_port)) {
1373         rh = rh->next;
1374     }
1375     if (rh && event_wakeup(rh->event_id) > 0)
1376         return;
1377
1378     /*
1379      * If we didn't find a handle, then check for a new incoming packet.
1380      * If no accept handler was setup, then just return.
1381      */
1382     if (udp->accept_fn == NULL)
1383         return;
1384
1385     he = gethostbyaddr((void *)&udp->peer.sin_addr,
1386         (socklen_t)sizeof(udp->peer.sin_addr), AF_INET);
1387     if (he == NULL)
1388         return;
1389     rh = alloc(SIZEOF(*rh));
1390     rh->proto_handle=NULL;
1391     rh->udp = udp;
1392     rh->rc = NULL;
1393     security_handleinit(&rh->sech, udp->driver);
1394     a = udp_inithandle(udp, rh,
1395                    he,
1396                    udp->peer.sin_port,
1397                    udp->handle,
1398                    udp->sequence);
1399     if (a < 0) {
1400         secprintf(("%s: bsd: closeX handle '%s'\n",
1401                   debug_prefix_time(NULL), rh->proto_handle));
1402
1403         amfree(rh);
1404         return;
1405     }
1406     /*
1407      * Check the security of the packet.  If it is bad, then pass NULL
1408      * to the accept function instead of a packet.
1409      */
1410     if (rh->udp->recv_security_ok(rh, &udp->pkt) < 0)
1411         (*udp->accept_fn)(&rh->sech, NULL);
1412     else
1413         (*udp->accept_fn)(&rh->sech, &udp->pkt);
1414 }
1415
1416 /*
1417  * Locate an existing connection to the given host, or create a new,
1418  * unconnected entry if none exists.  The caller is expected to check
1419  * for the lack of a connection (rc->read == -1) and set one up.
1420  */
1421 struct tcp_conn *
1422 sec_tcp_conn_get(
1423     const char *hostname,
1424     int         want_new)
1425 {
1426     struct tcp_conn *rc;
1427
1428     secprintf(("%s: sec_tcp_conn_get: %s\n", debug_prefix_time(NULL), hostname));
1429
1430     if (want_new == 0) {
1431     for (rc = connq_first(); rc != NULL; rc = connq_next(rc)) {
1432         if (strcasecmp(hostname, rc->hostname) == 0)
1433             break;
1434     }
1435
1436     if (rc != NULL) {
1437         rc->refcnt++;
1438         secprintf(("%s: sec_tcp_conn_get: exists, refcnt to %s is now %d\n",
1439                    debug_prefix_time(NULL),
1440                    rc->hostname, rc->refcnt));
1441         return (rc);
1442     }
1443     }
1444
1445     secprintf(("%s: sec_tcp_conn_get: creating new handle\n",
1446                debug_prefix_time(NULL)));
1447     /*
1448      * We can't be creating a new handle if we are the client
1449      */
1450     rc = alloc(SIZEOF(*rc));
1451     rc->read = rc->write = -1;
1452     rc->driver = NULL;
1453     rc->pid = -1;
1454     rc->ev_read = NULL;
1455     rc->toclose = 0;
1456     rc->donotclose = 0;
1457     strncpy(rc->hostname, hostname, SIZEOF(rc->hostname) - 1);
1458     rc->hostname[SIZEOF(rc->hostname) - 1] = '\0';
1459     rc->errmsg = NULL;
1460     rc->refcnt = 1;
1461     rc->handle = -1;
1462     rc->pkt = NULL;
1463     rc->accept_fn = NULL;
1464     rc->recv_security_ok = NULL;
1465     rc->prefix_packet = NULL;
1466     connq_append(rc);
1467     return (rc);
1468 }
1469
1470 /*
1471  * Delete a reference to a connection, and close it if it is the last
1472  * reference.
1473  */
1474 void
1475 sec_tcp_conn_put(
1476     struct tcp_conn *   rc)
1477 {
1478     amwait_t status;
1479
1480     assert(rc->refcnt > 0);
1481     --rc->refcnt;
1482     secprintf(("%s: sec_tcp_conn_put: decrementing refcnt for %s to %d\n",
1483                debug_prefix_time(NULL),
1484         rc->hostname, rc->refcnt));
1485     if (rc->refcnt > 0) {
1486         return;
1487     }
1488     secprintf(("%s: sec_tcp_conn_put: closing connection to %s\n",
1489                debug_prefix_time(NULL), rc->hostname));
1490     if (rc->read != -1)
1491         aclose(rc->read);
1492     if (rc->write != -1)
1493         aclose(rc->write);
1494     if (rc->pid != -1) {
1495         waitpid(rc->pid, &status, WNOHANG);
1496     }
1497     if (rc->ev_read != NULL)
1498         event_release(rc->ev_read);
1499     if (rc->errmsg != NULL)
1500         amfree(rc->errmsg);
1501     connq_remove(rc);
1502     amfree(rc->pkt);
1503     if(!rc->donotclose)
1504         amfree(rc); /* someone might still use it           */
1505                     /* eg. in sec_tcp_conn_read_callback if */
1506                     /*     event_wakeup call us.            */
1507 }
1508
1509 /*
1510  * Turn on read events for a conn.  Or, increase a ev_read_refcnt if we are
1511  * already receiving read events.
1512  */
1513 void
1514 sec_tcp_conn_read(
1515     struct tcp_conn *   rc)
1516 {
1517     assert (rc != NULL);
1518
1519     if (rc->ev_read != NULL) {
1520         rc->ev_read_refcnt++;
1521         secprintf((
1522                "%s: sec: conn_read: incremented ev_read_refcnt to %d for %s\n",
1523                debug_prefix_time(NULL), rc->ev_read_refcnt, rc->hostname));
1524         return;
1525     }
1526     secprintf(("%s: sec: conn_read registering event handler for %s\n",
1527                debug_prefix_time(NULL), rc->hostname));
1528     rc->ev_read = event_register((event_id_t)rc->read, EV_READFD,
1529                 sec_tcp_conn_read_callback, rc);
1530     rc->ev_read_refcnt = 1;
1531 }
1532
1533 static void
1534 sec_tcp_conn_read_cancel(
1535     struct tcp_conn *   rc)
1536 {
1537
1538     --rc->ev_read_refcnt;
1539     secprintf((
1540         "%s: sec: conn_read_cancel: decremented ev_read_refcnt to %d for %s\n",
1541         debug_prefix_time(NULL),
1542         rc->ev_read_refcnt, rc->hostname));
1543     if (rc->ev_read_refcnt > 0) {
1544         return;
1545     }
1546     secprintf(("%s: sec: conn_read_cancel: releasing event handler for %s\n",
1547                debug_prefix_time(NULL), rc->hostname));
1548     event_release(rc->ev_read);
1549     rc->ev_read = NULL;
1550 }
1551
1552 /*
1553  * This is called when a handle is woken up because data read off of the
1554  * net is for it.
1555  */
1556 static void
1557 recvpkt_callback(
1558     void *      cookie,
1559     void *      buf,
1560     ssize_t     bufsize)
1561 {
1562     pkt_t pkt;
1563     struct sec_handle *rh = cookie;
1564
1565     assert(rh != NULL);
1566
1567     secprintf(("%s: sec: recvpkt_callback: %d\n",
1568                debug_prefix_time(NULL), bufsize));
1569     /*
1570      * We need to cancel the recvpkt request before calling
1571      * the callback because the callback may reschedule us.
1572      */
1573     stream_recvpkt_cancel(rh);
1574
1575     switch (bufsize) {
1576     case 0:
1577         security_seterror(&rh->sech,
1578             "EOF on read from %s", rh->hostname);
1579         (*rh->fn.recvpkt)(rh->arg, NULL, S_ERROR);
1580         return;
1581     case -1:
1582         security_seterror(&rh->sech, security_stream_geterror(&rh->rs->secstr));
1583         (*rh->fn.recvpkt)(rh->arg, NULL, S_ERROR);
1584         return;
1585     default:
1586         break;
1587     }
1588
1589     parse_pkt(&pkt, buf, (size_t)bufsize);
1590     secprintf((
1591            "%s: sec: received %s packet (%d) from %s, contains:\n\n\"%s\"\n\n",
1592            debug_prefix_time(NULL), pkt_type2str(pkt.type), pkt.type,
1593            rh->hostname, pkt.body));
1594     if (rh->rc->recv_security_ok && (rh->rc->recv_security_ok)(rh, &pkt) < 0)
1595         (*rh->fn.recvpkt)(rh->arg, NULL, S_ERROR);
1596     else
1597         (*rh->fn.recvpkt)(rh->arg, &pkt, S_OK);
1598     amfree(pkt.body);
1599 }
1600
1601 /*
1602  * Callback for tcpm_stream_read_sync
1603  */
1604 static void
1605 stream_read_sync_callback(
1606     void *      s)
1607 {
1608     struct sec_stream *rs = s;
1609     assert(rs != NULL);
1610
1611     secprintf(("%s: sec: stream_read_callback_sync: handle %d\n",
1612                debug_prefix_time(NULL), rs->handle));
1613
1614     /*
1615      * Make sure this was for us.  If it was, then blow away the handle
1616      * so it doesn't get claimed twice.  Otherwise, leave it alone.
1617      *
1618      * If the handle is EOF, pass that up to our callback.
1619      */
1620     if (rs->rc->handle == rs->handle) {
1621         secprintf(("%s: sec: stream_read_callback_sync: it was for us\n",
1622                    debug_prefix_time(NULL)));
1623         rs->rc->handle = H_TAKEN;
1624     } else if (rs->rc->handle != H_EOF) {
1625         secprintf(("%s: sec: stream_read_callback_sync: not for us\n",
1626                    debug_prefix_time(NULL)));
1627         return;
1628     }
1629
1630     /*
1631      * Remove the event first, and then call the callback.
1632      * We remove it first because we don't want to get in their
1633      * way if they reschedule it.
1634      */
1635     tcpm_stream_read_cancel(rs);
1636
1637     if (rs->rc->pktlen <= 0) {
1638         secprintf(("%s: sec: stream_read_sync_callback: %s\n",
1639                    debug_prefix_time(NULL), rs->rc->errmsg));
1640         security_stream_seterror(&rs->secstr, rs->rc->errmsg);
1641         if(rs->closed_by_me == 0 && rs->closed_by_network == 0)
1642             sec_tcp_conn_put(rs->rc);
1643         rs->closed_by_network = 1;
1644         return;
1645     }
1646     secprintf((
1647              "%s: sec: stream_read_callback_sync: read %ld bytes from %s:%d\n",
1648              debug_prefix_time(NULL),
1649         rs->rc->pktlen, rs->rc->hostname, rs->handle));
1650 }
1651
1652 /*
1653  * Callback for tcpm_stream_read
1654  */
1655 static void
1656 stream_read_callback(
1657     void *      arg)
1658 {
1659     struct sec_stream *rs = arg;
1660     assert(rs != NULL);
1661
1662     secprintf(("%s: sec: stream_read_callback: handle %d\n",
1663                debug_prefix_time(NULL), rs->handle));
1664
1665     /*
1666      * Make sure this was for us.  If it was, then blow away the handle
1667      * so it doesn't get claimed twice.  Otherwise, leave it alone.
1668      *
1669      * If the handle is EOF, pass that up to our callback.
1670      */
1671     if (rs->rc->handle == rs->handle) {
1672         secprintf(("%s: sec: stream_read_callback: it was for us\n",
1673                    debug_prefix_time(NULL)));
1674         rs->rc->handle = H_TAKEN;
1675     } else if (rs->rc->handle != H_EOF) {
1676         secprintf(("%s: sec: stream_read_callback: not for us\n",
1677                    debug_prefix_time(NULL)));
1678         return;
1679     }
1680
1681     /*
1682      * Remove the event first, and then call the callback.
1683      * We remove it first because we don't want to get in their
1684      * way if they reschedule it.
1685      */
1686     tcpm_stream_read_cancel(rs);
1687
1688     if (rs->rc->pktlen <= 0) {
1689         secprintf(("%s: sec: stream_read_callback: %s\n",
1690                    debug_prefix_time(NULL), rs->rc->errmsg));
1691         security_stream_seterror(&rs->secstr, rs->rc->errmsg);
1692         if(rs->closed_by_me == 0 && rs->closed_by_network == 0)
1693             sec_tcp_conn_put(rs->rc);
1694         rs->closed_by_network = 1;
1695         (*rs->fn)(rs->arg, NULL, rs->rc->pktlen);
1696         return;
1697     }
1698     secprintf(("%s: sec: stream_read_callback: read %ld bytes from %s:%d\n",
1699                debug_prefix_time(NULL),
1700         rs->rc->pktlen, rs->rc->hostname, rs->handle));
1701     (*rs->fn)(rs->arg, rs->rc->pkt, rs->rc->pktlen);
1702     secprintf(("%s: sec: after callback stream_read_callback\n",
1703                debug_prefix_time(NULL)));
1704 }
1705
1706 /*
1707  * The callback for the netfd for the event handler
1708  * Determines if this packet is for this security handle,
1709  * and does the real callback if so.
1710  */
1711 static void
1712 sec_tcp_conn_read_callback(
1713     void *      cookie)
1714 {
1715     struct tcp_conn *   rc = cookie;
1716     struct sec_handle * rh;
1717     pkt_t               pkt;
1718     ssize_t             rval;
1719     int                 revent;
1720
1721     assert(cookie != NULL);
1722
1723     secprintf(("%s: sec: conn_read_callback\n", debug_prefix_time(NULL)));
1724
1725     /* Read the data off the wire.  If we get errors, shut down. */
1726     rval = tcpm_recv_token(rc->read, &rc->handle, &rc->errmsg, &rc->pkt,
1727                                 &rc->pktlen, 60);
1728     secprintf(("%s: sec: conn_read_callback: tcpm_recv_token returned %d\n",
1729                debug_prefix_time(NULL), rval));
1730     if (rval < 0 || rc->handle == H_EOF) {
1731         rc->pktlen = rval;
1732         rc->handle = H_EOF;
1733         revent = event_wakeup((event_id_t)rc);
1734         secprintf(("%s: sec: conn_read_callback: event_wakeup return %d\n",
1735                    debug_prefix_time(NULL), revent));
1736         /* delete our 'accept' reference */
1737         if (rc->accept_fn != NULL) {
1738             if(rc->refcnt != 1) {
1739                 dbprintf(("STRANGE, rc->refcnt should be 1"));
1740                 rc->refcnt=1;
1741             }
1742             rc->accept_fn = NULL;
1743             sec_tcp_conn_put(rc);
1744         }
1745         return;
1746     }
1747
1748     if(rval == 0) {
1749         rc->pktlen = 0;
1750         revent = event_wakeup((event_id_t)rc);
1751         secprintf(("%s: 0 sec: conn_read_callback: event_wakeup return %d\n",
1752                    debug_prefix_time(NULL), revent));
1753         return;
1754     }
1755
1756     /* If there are events waiting on this handle, we're done */
1757     rc->donotclose = 1;
1758     revent = event_wakeup((event_id_t)rc);
1759     secprintf(("%s: sec: conn_read_callback: event_wakeup return %d\n",
1760                debug_prefix_time(NULL), rval));
1761     rc->donotclose = 0;
1762     if (rc->handle == H_TAKEN || rc->pktlen == 0) {
1763         if(rc->refcnt == 0) amfree(rc);
1764         return;
1765     }
1766
1767     assert(rc->refcnt > 0);
1768
1769     /* If there is no accept fn registered, then drop the packet */
1770     if (rc->accept_fn == NULL)
1771         return;
1772
1773     rh = alloc(SIZEOF(*rh));
1774     security_handleinit(&rh->sech, rc->driver);
1775     rh->hostname = stralloc(rc->hostname);
1776     rh->ev_timeout = NULL;
1777     rh->rc = rc;
1778     rh->peer = rc->peer;
1779     rh->rs = tcpma_stream_client(rh, rc->handle);
1780
1781     secprintf(("%s: sec: new connection\n", debug_prefix_time(NULL)));
1782     pkt.body = NULL;
1783     parse_pkt(&pkt, rc->pkt, (size_t)rc->pktlen);
1784     secprintf(("%s: sec: calling accept_fn\n", debug_prefix_time(NULL)));
1785     if (rh->rc->recv_security_ok && (rh->rc->recv_security_ok)(rh, &pkt) < 0)
1786         (*rc->accept_fn)(&rh->sech, NULL);
1787     else
1788         (*rc->accept_fn)(&rh->sech, &pkt);
1789     amfree(pkt.body);
1790 }
1791
1792 void
1793 parse_pkt(
1794     pkt_t *     pkt,
1795     const void *buf,
1796     size_t      bufsize)
1797 {
1798     const unsigned char *bufp = buf;
1799
1800     secprintf(("%s: sec: parse_pkt: parsing buffer of %d bytes\n",
1801                debug_prefix_time(NULL), bufsize));
1802
1803     pkt->type = (pktype_t)*bufp++;
1804     bufsize--;
1805
1806     pkt->packet_size = bufsize+1;
1807     pkt->body = alloc(pkt->packet_size);
1808     if (bufsize == 0) {
1809         pkt->body[0] = '\0';
1810     } else {
1811         memcpy(pkt->body, bufp, bufsize);
1812         pkt->body[pkt->packet_size - 1] = '\0';
1813     }
1814     pkt->size = strlen(pkt->body);
1815
1816     secprintf(("%s: sec: parse_pkt: %s (%d): \"%s\"\n",
1817                debug_prefix_time(NULL), pkt_type2str(pkt->type),
1818                pkt->type, pkt->body));
1819 }
1820
1821 /*
1822  * Convert a packet header into a string
1823  */
1824 const char *
1825 pkthdr2str(
1826     const struct sec_handle *   rh,
1827     const pkt_t *               pkt)
1828 {
1829     static char retbuf[256];
1830
1831     assert(rh != NULL);
1832     assert(pkt != NULL);
1833
1834     snprintf(retbuf, SIZEOF(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n",
1835         VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type),
1836         rh->proto_handle, rh->sequence);
1837
1838     secprintf(("%s: bsd: pkthdr2str handle '%s'\n",
1839                debug_prefix_time(NULL), rh->proto_handle));
1840
1841     /* check for truncation.  If only we had asprintf()... */
1842     assert(retbuf[strlen(retbuf) - 1] == '\n');
1843
1844     return (retbuf);
1845 }
1846
1847 /*
1848  * Parses out the header line in 'str' into the pkt and handle
1849  * Returns negative on parse error.
1850  */
1851 int
1852 str2pkthdr(
1853     udp_handle_t *      udp)
1854 {
1855     char *str;
1856     const char *tok;
1857     pkt_t *pkt;
1858
1859     pkt = &udp->pkt;
1860
1861     assert(udp->dgram.cur != NULL);
1862     str = stralloc(udp->dgram.cur);
1863
1864     /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */
1865
1866     /* Read in "Amanda" */
1867     if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0)
1868         goto parse_error;
1869
1870     /* nothing is done with the major/minor numbers currently */
1871     if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL)
1872         goto parse_error;
1873
1874     /* Read in the packet type */
1875     if ((tok = strtok(NULL, " ")) == NULL)
1876         goto parse_error;
1877     amfree(pkt->body);
1878     pkt_init(pkt, pkt_str2type(tok), "");
1879     if (pkt->type == (pktype_t)-1)    
1880         goto parse_error;
1881
1882     /* Read in "HANDLE" */
1883     if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0)
1884         goto parse_error;
1885
1886     /* parse the handle */
1887     if ((tok = strtok(NULL, " ")) == NULL)
1888         goto parse_error;
1889     amfree(udp->handle);
1890     udp->handle = stralloc(tok);
1891
1892     /* Read in "SEQ" */
1893     if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0)   
1894         goto parse_error;
1895
1896     /* parse the sequence number */   
1897     if ((tok = strtok(NULL, "\n")) == NULL)
1898         goto parse_error;
1899     udp->sequence = atoi(tok);
1900
1901     /* Save the body, if any */       
1902     if ((tok = strtok(NULL, "")) != NULL)
1903         pkt_cat(pkt, "%s", tok);
1904
1905     amfree(str);
1906     return (0);
1907
1908 parse_error:
1909 #if 0 /* XXX we have no way of passing this back up */
1910     security_seterror(&rh->sech,
1911         "parse error in packet header : '%s'", origstr);
1912 #endif
1913     amfree(str);
1914     return (-1);
1915 }
1916
1917 char *
1918 check_user(
1919     struct sec_handle * rh,
1920     const char *        remoteuser,
1921     const char *        service)
1922 {
1923     struct passwd *pwd;
1924     char *r;
1925     char *result = NULL;
1926     char *localuser = NULL;
1927
1928     /* lookup our local user name */
1929     if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
1930         return vstralloc("getpwnam(", CLIENT_LOGIN, ") fails", NULL);
1931     }
1932
1933     /*
1934      * Make a copy of the user name in case getpw* is called by
1935      * any of the lower level routines.
1936      */
1937     localuser = stralloc(pwd->pw_name);
1938
1939 #ifndef USE_AMANDAHOSTS
1940     r = check_user_ruserok(rh->hostname, pwd, remoteuser);
1941 #else
1942     r = check_user_amandahosts(rh->hostname, rh->peer.sin_addr, pwd, remoteuser, service);
1943 #endif
1944     if (r != NULL) {
1945         result = vstralloc("user ", remoteuser, " from ", rh->hostname,
1946                            " is not allowed to execute the service ",
1947                            service, ": ", r, NULL);
1948         amfree(r);
1949     }
1950     amfree(localuser);
1951     return result;
1952 }
1953
1954 /*
1955  * See if a remote user is allowed in.  This version uses ruserok()
1956  * and friends.
1957  *
1958  * Returns 0 on success, or negative on error.
1959  */
1960 char *
1961 check_user_ruserok(
1962     const char *        host,
1963     struct passwd *     pwd,
1964     const char *        remoteuser)
1965 {
1966     int saved_stderr;
1967     int fd[2];
1968     FILE *fError;
1969     amwait_t exitcode;
1970     pid_t ruserok_pid;
1971     pid_t pid;
1972     char *es;
1973     char *result;
1974     int ok;
1975     char number[NUM_STR_SIZE];
1976     uid_t myuid = getuid();
1977
1978     /*
1979      * note that some versions of ruserok (eg SunOS 3.2) look in
1980      * "./.rhosts" rather than "~CLIENT_LOGIN/.rhosts", so we have to
1981      * chdir ourselves.  Sigh.
1982      *
1983      * And, believe it or not, some ruserok()'s try an initgroup just
1984      * for the hell of it.  Since we probably aren't root at this point
1985      * it'll fail, and initgroup "helpfully" will blatt "Setgroups: Not owner"
1986      * into our stderr output even though the initgroup failure is not a
1987      * problem and is expected.  Thanks a lot.  Not.
1988      */
1989     if (pipe(fd) != 0) {
1990         return stralloc2("pipe() fails: ", strerror(errno));
1991     }
1992     if ((ruserok_pid = fork()) < 0) {
1993         return stralloc2("fork() fails: ", strerror(errno));
1994     } else if (ruserok_pid == 0) {
1995         int ec;
1996
1997         close(fd[0]);
1998         fError = fdopen(fd[1], "w");
1999         if (!fError) {
2000             error("Can't fdopen: %s", strerror(errno));
2001             /*NOTREACHED*/
2002         }
2003         /* pamper braindead ruserok's */
2004         if (chdir(pwd->pw_dir) != 0) {
2005             fprintf(fError, "chdir(%s) failed: %s",
2006                     pwd->pw_dir, strerror(errno));
2007             fclose(fError);
2008             exit(1);
2009         }
2010
2011 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
2012         {
2013         char *dir = stralloc(pwd->pw_dir);
2014
2015         secprintf(("%s: bsd: calling ruserok(%s, %d, %s, %s)\n",
2016                    debug_prefix_time(NULL),
2017                    host, ((myuid == 0) ? 1 : 0), remoteuser, pwd->pw_name));
2018         if (myuid == 0) {
2019             secprintf(("%s: bsd: because you are running as root, ",
2020                        debug_prefix(NULL)));
2021             secprintf(("/etc/hosts.equiv will not be used\n"));
2022         } else {
2023             show_stat_info("/etc/hosts.equiv", NULL);
2024         }
2025         show_stat_info(dir, "/.rhosts");
2026         amfree(dir);
2027         }
2028 #endif                                                          /* } */
2029
2030         saved_stderr = dup(2);
2031         close(2);
2032         if (open("/dev/null", O_RDWR) == -1) {
2033             secprintf(("%s: Could not open /dev/null: %s\n",
2034                       debug_prefix(NULL), strerror(errno)));
2035             ec = 1;
2036         } else {
2037             ok = ruserok(host, myuid == 0, remoteuser, CLIENT_LOGIN);
2038             if (ok < 0) {
2039                 ec = 1;
2040             } else {
2041                 ec = 0;
2042             }
2043         }
2044         (void)dup2(saved_stderr,2);
2045         close(saved_stderr);
2046         exit(ec);
2047     }
2048     close(fd[1]);
2049     fError = fdopen(fd[0], "r");
2050     if (!fError) {
2051         error("Can't fdopen: %s", strerror(errno));
2052         /*NOTREACHED*/
2053     }
2054
2055     result = NULL;
2056     while ((es = agets(fError)) != NULL) {
2057         if (*es == 0) {
2058             amfree(es);
2059             continue;
2060         }
2061         if (result == NULL) {
2062             result = stralloc("");
2063         } else {
2064             strappend(result, ": ");
2065         }
2066         strappend(result, es);
2067         amfree(es);
2068     }
2069     close(fd[0]);
2070
2071     pid = wait(&exitcode);
2072     while (pid != ruserok_pid) {
2073         if ((pid == (pid_t) -1) && (errno != EINTR)) {
2074             amfree(result);
2075             return stralloc2("ruserok wait failed: %s", strerror(errno));
2076         }
2077         pid = wait(&exitcode);
2078     }
2079     if (WIFSIGNALED(exitcode)) {
2080         amfree(result);
2081         snprintf(number, SIZEOF(number), "%d", WTERMSIG(exitcode));
2082         return stralloc2("ruserok child got signal ", number);
2083     }
2084     if (WEXITSTATUS(exitcode) == 0) {
2085         amfree(result);
2086     } else if (result == NULL) {
2087         result = stralloc("ruserok failed");
2088     }
2089
2090     return result;
2091 }
2092
2093 /*
2094  * Check to see if a user is allowed in.  This version uses .amandahosts
2095  * Returns -1 on failure, or 0 on success.
2096  */
2097 char *
2098 check_user_amandahosts(
2099     const char *        host,
2100     struct in_addr      addr,
2101     struct passwd *     pwd,
2102     const char *        remoteuser,
2103     const char *        service)
2104 {
2105     char *line = NULL;
2106     char *filehost;
2107     const char *fileuser;
2108     char *ptmp = NULL;
2109     char *result = NULL;
2110     FILE *fp = NULL;
2111     int found;
2112     struct stat sbuf;
2113     char n1[NUM_STR_SIZE];
2114     char n2[NUM_STR_SIZE];
2115     int hostmatch;
2116     int usermatch;
2117     char *aservice = NULL;
2118
2119     secprintf(("check_user_amandahosts(host=%s, pwd=%p, "
2120                 "remoteuser=%s, service=%s)\n",
2121                 host, pwd, remoteuser, service));
2122
2123     ptmp = stralloc2(pwd->pw_dir, "/.amandahosts");
2124 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
2125     show_stat_info(ptmp, "");;
2126 #endif                                                          /* } */
2127     if ((fp = fopen(ptmp, "r")) == NULL) {
2128         result = vstralloc("cannot open ", ptmp, ": ", strerror(errno), NULL);
2129         amfree(ptmp);
2130         return result;
2131     }
2132
2133     /*
2134      * Make sure the file is owned by the Amanda user and does not
2135      * have any group/other access allowed.
2136      */
2137     if (fstat(fileno(fp), &sbuf) != 0) {
2138         result = vstralloc("cannot fstat ", ptmp, ": ", strerror(errno), NULL);
2139         goto common_exit;
2140     }
2141     if (sbuf.st_uid != pwd->pw_uid) {
2142         snprintf(n1, SIZEOF(n1), "%ld", (long)sbuf.st_uid);
2143         snprintf(n2, SIZEOF(n2), "%ld", (long)pwd->pw_uid);
2144         result = vstralloc(ptmp, ": ",
2145                            "owned by id ", n1,
2146                            ", should be ", n2,
2147                            NULL);
2148         goto common_exit;
2149     }
2150     if ((sbuf.st_mode & 077) != 0) {
2151         result = stralloc2(ptmp,
2152           ": incorrect permissions; file must be accessible only by its owner");
2153         goto common_exit;
2154     }
2155
2156     /*
2157      * Now, scan the file for the host/user/service.
2158      */
2159     found = 0;
2160     while ((line = agets(fp)) != NULL) {
2161         if (*line == 0) {
2162             amfree(line);
2163             continue;
2164         }
2165
2166 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
2167         secprintf(("%s: bsd: processing line: <%s>\n", debug_prefix(NULL), line));
2168 #endif                                                          /* } */
2169         /* get the host out of the file */
2170         if ((filehost = strtok(line, " \t")) == NULL) {
2171             amfree(line);
2172             continue;
2173         }
2174
2175         /* get the username.  If no user specified, then use the local user */
2176         if ((fileuser = strtok(NULL, " \t")) == NULL) {
2177             fileuser = pwd->pw_name;
2178         }
2179
2180         hostmatch = (strcasecmp(filehost, host) == 0);
2181         /*  ok if addr=127.0.0.1 and
2182         *  either localhost or localhost.domain is in .amandahost */
2183        if ( !hostmatch ) {
2184          if (strcmp(inet_ntoa(addr), "127.0.0.1")== 0  &&
2185              (strcasecmp(filehost, "localhost")== 0 ||
2186               strcasecmp(filehost, "localhost.localdomain")== 0))
2187            {
2188              hostmatch = 1;
2189            }
2190        }
2191         usermatch = (strcasecmp(fileuser, remoteuser) == 0);
2192 #if defined(SHOW_SECURITY_DETAIL)                               /* { */
2193         secprintf(("%s: bsd: comparing \"%s\" with\n", debug_prefix(NULL), filehost));
2194         secprintf(("%s: bsd:           \"%s\" (%s)\n", host,
2195                   debug_prefix(NULL), hostmatch ? "match" : "no match"));
2196         secprintf(("%s: bsd:       and \"%s\" with\n", fileuser, debug_prefix(NULL)));
2197         secprintf(("%s: bsd:           \"%s\" (%s)\n", remoteuser,
2198                   debug_prefix(NULL), usermatch ? "match" : "no match"));
2199 #endif                                                          /* } */
2200         /* compare */
2201         if (!hostmatch || !usermatch) {
2202             amfree(line);
2203             continue;
2204         }
2205
2206         if (!service) {
2207             /* success */
2208             amfree(line);
2209             found = 1;
2210             break;
2211         }
2212
2213         /* get the services.  If no service specified, then use
2214          * noop/selfcheck/sendsize/sendbackup
2215          */
2216         aservice = strtok(NULL, " \t,");
2217         if (!aservice) {
2218             if (strcmp(service,"noop") == 0 ||
2219                strcmp(service,"selfcheck") == 0 ||
2220                strcmp(service,"sendsize") == 0 ||
2221                strcmp(service,"sendbackup") == 0) {
2222                 /* success */
2223                 found = 1;
2224                 amfree(line);
2225                 break;
2226             }
2227             else {
2228                 amfree(line);
2229                 break;
2230             }
2231         }
2232
2233         do {
2234             if (strcmp(aservice,service) == 0) {
2235                 found = 1;
2236                 break;
2237             }
2238             if (strcmp(aservice, "amdump") == 0 && 
2239                (strcmp(service, "noop") == 0 ||
2240                 strcmp(service, "selfcheck") == 0 ||
2241                 strcmp(service, "sendsize") == 0 ||
2242                 strcmp(service, "sendbackup") == 0)) {
2243                 found = 1;
2244                 break;
2245             }
2246         } while((aservice = strtok(NULL, " \t,")) != NULL);
2247
2248         if (aservice && strcmp(aservice, service) == 0) {
2249             /* success */
2250             found = 1;
2251             amfree(line);
2252             break;
2253         }
2254         amfree(line);
2255     }
2256     if (! found) {
2257         if (strcmp(service, "amindexd") == 0 ||
2258             strcmp(service, "amidxtaped") == 0) {
2259             result = vstralloc("Please add \"amindexd amidxtaped\" to "
2260                                "the line in ", ptmp, NULL);
2261         } else if (strcmp(service, "amdump") == 0 ||
2262                    strcmp(service, "noop") == 0 ||
2263                    strcmp(service, "selfcheck") == 0 ||
2264                    strcmp(service, "sendsize") == 0 ||
2265                    strcmp(service, "sendbackup") == 0) {
2266             result = vstralloc("Please add \"amdump\" to the line in ",
2267                                ptmp, NULL);
2268         } else {
2269             result = vstralloc(ptmp, ": ",
2270                                "invalid service ", service, NULL);
2271         }
2272     }
2273
2274 common_exit:
2275
2276     afclose(fp);
2277     amfree(ptmp);
2278
2279     return result;
2280 }
2281
2282 /* return 1 on success, 0 on failure */
2283 int
2284 check_security(
2285     struct sockaddr_in *addr,
2286     char *              str,
2287     unsigned long       cksum,
2288     char **             errstr)
2289 {
2290     char *              remotehost = NULL, *remoteuser = NULL;
2291     char *              bad_bsd = NULL;
2292     struct hostent *    hp;
2293     struct passwd *     pwptr;
2294     uid_t               myuid;
2295     int                 i;
2296     int                 j;
2297     char *              s;
2298     char *              fp;
2299     int                 ch;
2300
2301     (void)cksum;        /* Quiet unused parameter warning */
2302
2303     secprintf(("%s: check_security(addr=%p, str='%s', cksum=%ul, errstr=%p\n",
2304                 debug_prefix(NULL), addr, str, cksum, errstr));
2305     dump_sockaddr(addr);
2306
2307     *errstr = NULL;
2308
2309     /* what host is making the request? */
2310
2311     hp = gethostbyaddr((char *)&addr->sin_addr, SIZEOF(addr->sin_addr),
2312                        AF_INET);
2313     if (hp == NULL) {
2314         /* XXX include remote address in message */
2315         *errstr = vstralloc("[",
2316                             "addr ", inet_ntoa(addr->sin_addr), ": ",
2317                             "hostname lookup failed",
2318                             "]", NULL);
2319         return 0;
2320     }
2321     remotehost = stralloc(hp->h_name);
2322
2323     /* Now let's get the hostent for that hostname */
2324     hp = gethostbyname( remotehost );
2325     if (hp == NULL) {
2326         /* XXX include remote hostname in message */
2327         *errstr = vstralloc("[",
2328                             "host ", remotehost, ": ",
2329                             "hostname lookup failed",
2330                             "]", NULL);
2331         amfree(remotehost);
2332         return 0;
2333     }
2334
2335     /* Verify that the hostnames match -- they should theoretically */
2336     if (strncasecmp( remotehost, hp->h_name, strlen(remotehost)+1 ) != 0 ) {
2337         *errstr = vstralloc("[",
2338                             "hostnames do not match: ",
2339                             remotehost, " ", hp->h_name,
2340                             "]", NULL);
2341         amfree(remotehost);
2342         return 0;
2343     }
2344
2345     /* Now let's verify that the ip which gave us this hostname
2346      * is really an ip for this hostname; or is someone trying to
2347      * break in? (THIS IS THE CRUCIAL STEP)
2348      */
2349     for (i = 0; hp->h_addr_list[i]; i++) {
2350         if (memcmp(hp->h_addr_list[i],
2351                    (char *) &addr->sin_addr, SIZEOF(addr->sin_addr)) == 0)
2352             break;                     /* name is good, keep it */
2353     }
2354
2355     /* If we did not find it, your DNS is messed up or someone is trying
2356      * to pull a fast one on you. :(
2357      */
2358
2359    /*   Check even the aliases list. Work around for Solaris if dns goes over NIS */
2360
2361     if (!hp->h_addr_list[i] ) {
2362         for (j = 0; hp->h_aliases[j] !=0 ; j++) {
2363              if (strcmp(hp->h_aliases[j],inet_ntoa(addr->sin_addr)) == 0)
2364                  break;                          /* name is good, keep it */
2365         }
2366         if (!hp->h_aliases[j] ) {
2367             *errstr = vstralloc("[",
2368                                 "ip address ", inet_ntoa(addr->sin_addr),
2369                                 " is not in the ip list for ", remotehost,
2370                                 "]",
2371                                 NULL);
2372             amfree(remotehost);
2373             return 0;
2374         }
2375     }
2376
2377     /* next, make sure the remote port is a "reserved" one */
2378
2379     if (ntohs(addr->sin_port) >= IPPORT_RESERVED) {
2380         char number[NUM_STR_SIZE];
2381
2382         snprintf(number, SIZEOF(number), "%hd", (short)ntohs(addr->sin_port));
2383         *errstr = vstralloc("[",
2384                             "host ", remotehost, ": ",
2385                             "port ", number, " not secure",
2386                             "]", NULL);
2387         amfree(remotehost);
2388         return 0;
2389     }
2390
2391     /* extract the remote user name from the message */
2392
2393     s = str;
2394     ch = *s++;
2395
2396     bad_bsd = vstralloc("[",
2397                         "host ", remotehost, ": ",
2398                         "bad bsd security line",
2399                         "]", NULL);
2400
2401 #define sc "USER "
2402     if (strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
2403         *errstr = bad_bsd;
2404         bad_bsd = NULL;
2405         amfree(remotehost);
2406         return 0;
2407     }
2408     s += SIZEOF(sc)-1;
2409     ch = s[-1];
2410 #undef sc
2411
2412     skip_whitespace(s, ch);
2413     if (ch == '\0') {
2414         *errstr = bad_bsd;
2415         bad_bsd = NULL;
2416         amfree(remotehost);
2417         return 0;
2418     }
2419     fp = s - 1;
2420     skip_non_whitespace(s, ch);
2421     s[-1] = '\0';
2422     remoteuser = stralloc(fp);
2423     s[-1] = (char)ch;
2424     amfree(bad_bsd);
2425
2426     /* lookup our local user name */
2427
2428     myuid = getuid();
2429     if ((pwptr = getpwuid(myuid)) == NULL)
2430         error("error [getpwuid(%d) fails]", myuid);
2431
2432     secprintf(("%s: bsd security: remote host %s user %s local user %s\n",
2433               debug_prefix(NULL), remotehost, remoteuser, pwptr->pw_name));
2434
2435 #ifndef USE_AMANDAHOSTS
2436     s = check_user_ruserok(remotehost, pwptr, remoteuser);
2437 #else
2438     s = check_user_amandahosts(remotehost, addr->sin_addr, pwptr, remoteuser, NULL);
2439 #endif
2440
2441     if (s != NULL) {
2442         *errstr = vstralloc("[",
2443                             "access as ", pwptr->pw_name, " not allowed",
2444                             " from ", remoteuser, "@", remotehost,
2445                             ": ", s, "]", NULL);
2446     }
2447     amfree(s);
2448     amfree(remotehost);
2449     amfree(remoteuser);
2450     return *errstr == NULL;
2451 }
2452
2453 /*
2454  * Writes out the entire iovec
2455  */
2456 ssize_t
2457 net_writev(
2458     int                 fd,
2459     struct iovec *      iov,
2460     int                 iovcnt)
2461 {
2462     ssize_t delta, n, total;
2463
2464     assert(iov != NULL);
2465
2466     total = 0;
2467     while (iovcnt > 0) {
2468         /*
2469          * Write the iovec
2470          */
2471         n = writev(fd, iov, iovcnt);
2472         if (n < 0) {
2473             if (errno != EINTR)
2474                 return (-1);
2475             secprintf(("%s: net_writev got EINTR\n",
2476                        debug_prefix(NULL)));
2477         }
2478         else if (n == 0) {
2479             errno = EIO;
2480             return (-1);
2481         } else {
2482             total += n;
2483             /*
2484              * Iterate through each iov.  Figure out what we still need
2485              * to write out.
2486              */
2487             for (; n > 0; iovcnt--, iov++) {
2488                 /* 'delta' is the bytes written from this iovec */
2489                 delta = ((size_t)n < iov->iov_len) ? n : (ssize_t)iov->iov_len;
2490                 /* subtract from the total num bytes written */
2491                 n -= delta;
2492                 assert(n >= 0);
2493                 /* subtract from this iovec */
2494                 iov->iov_len -= delta;
2495                 iov->iov_base = (char *)iov->iov_base + delta;
2496                 /* if this iovec isn't empty, run the writev again */
2497                 if (iov->iov_len > 0)
2498                     break;
2499             }
2500         }
2501     }
2502     return (total);
2503 }
2504
2505 /*
2506  * Like read(), but waits until the entire buffer has been filled.
2507  */
2508 ssize_t
2509 net_read(
2510     int         fd,
2511     void *      vbuf,
2512     size_t      origsize,
2513     int         timeout)
2514 {
2515     char *buf = vbuf;   /* ptr arith */
2516     ssize_t nread;
2517     size_t size = origsize;
2518
2519     secprintf(("%s: net_read: begin %d\n", debug_prefix_time(NULL), origsize));
2520
2521     while (size > 0) {
2522         secprintf(("%s: net_read: while %d\n",
2523                    debug_prefix_time(NULL), size));
2524         nread = net_read_fillbuf(fd, timeout, buf, size);
2525         if (nread < 0) {
2526             secprintf(("%s: db: net_read: end return(-1)\n",
2527                        debug_prefix_time(NULL)));
2528             return (-1);
2529         }
2530         if (nread == 0) {
2531             secprintf(("%s: net_read: end return(0)\n",
2532                        debug_prefix_time(NULL)));
2533             return (0);
2534         }
2535         buf += nread;
2536         size -= nread;
2537     }
2538     secprintf(("%s: net_read: end %d\n",
2539                debug_prefix_time(NULL), origsize));
2540     return ((ssize_t)origsize);
2541 }
2542
2543 /*
2544  * net_read likes to do a lot of little reads.  Buffer it.
2545  */
2546 ssize_t
2547 net_read_fillbuf(
2548     int         fd,
2549     int         timeout,
2550     void *      buf,
2551     size_t      size)
2552 {
2553     fd_set readfds;
2554     struct timeval tv;
2555     ssize_t nread;
2556
2557     secprintf(("%s: net_read_fillbuf: begin\n", debug_prefix_time(NULL)));
2558     FD_ZERO(&readfds);
2559     FD_SET(fd, &readfds);
2560     tv.tv_sec = timeout;
2561     tv.tv_usec = 0;
2562     switch (select(fd + 1, &readfds, NULL, NULL, &tv)) {
2563     case 0:
2564         errno = ETIMEDOUT;
2565         /* FALLTHROUGH */
2566     case -1:
2567         secprintf(("%s: net_read_fillbuf: case -1\n",
2568                    debug_prefix_time(NULL)));
2569         return (-1);
2570     case 1:
2571         secprintf(("%s: net_read_fillbuf: case 1\n",
2572                    debug_prefix_time(NULL)));
2573         assert(FD_ISSET(fd, &readfds));
2574         break;
2575     default:
2576         secprintf(("%s: net_read_fillbuf: case default\n",
2577                    debug_prefix_time(NULL)));
2578         assert(0);
2579         break;
2580     }
2581     nread = read(fd, buf, size);
2582     if (nread < 0)
2583         return (-1);
2584     secprintf(("%s: net_read_fillbuf: end %d\n",
2585                debug_prefix_time(NULL), nread));
2586     return (nread);
2587 }
2588
2589
2590 /*
2591  * Display stat() information about a file.
2592  */
2593
2594 void
2595 show_stat_info(
2596     char *      a,
2597     char *      b)
2598 {
2599     char *name = vstralloc(a, b, NULL);
2600     struct stat sbuf;
2601     struct passwd *pwptr;
2602     char *owner;
2603     struct group *grptr;
2604     char *group;
2605
2606     if (stat(name, &sbuf) != 0) {
2607         secprintf(("%s: bsd: cannot stat %s: %s\n",
2608                    debug_prefix_time(NULL), name, strerror(errno)));
2609         amfree(name);
2610         return;
2611     }
2612     if ((pwptr = getpwuid(sbuf.st_uid)) == NULL) {
2613         owner = alloc(NUM_STR_SIZE + 1);
2614         snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_uid);
2615     } else {
2616         owner = stralloc(pwptr->pw_name);
2617     }
2618     if ((grptr = getgrgid(sbuf.st_gid)) == NULL) {
2619         group = alloc(NUM_STR_SIZE + 1);
2620         snprintf(owner, NUM_STR_SIZE, "%ld", (long)sbuf.st_gid);
2621     } else {
2622         group = stralloc(grptr->gr_name);
2623     }
2624     secprintf(("%s: bsd: processing file: %s\n", debug_prefix(NULL), name));
2625     secprintf(("%s: bsd:                  owner=%s group=%s mode=%03o\n",
2626                debug_prefix(NULL), owner, group, (int) (sbuf.st_mode & 0777)));
2627     amfree(name);
2628     amfree(owner);
2629     amfree(group);
2630 }