]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
fixes
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif /* HAVE_UNISTD_H */
33 #include <ctype.h>
34 #include <atalk/logger.h>
35 #include <atalk/util.h>
36
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40
41 #include <atalk/dsi.h>
42 #include <atalk/atp.h>
43 #include <atalk/asp.h>
44 #include <atalk/nbp.h>
45 #include <atalk/afp.h>
46 #include <atalk/compat.h>
47 #include <atalk/server_child.h>
48 #ifdef USE_SRVLOC
49 #include <slp.h>
50 #endif /* USE_SRVLOC */
51 #ifdef HAVE_ACLS
52 #include <atalk/ldapconfig.h>
53 #endif
54
55 #include "globals.h"
56 #include "afp_config.h"
57 #include "uam_auth.h"
58 #include "status.h"
59
60 #define LINESIZE 1024  
61
62 /* get rid of unneeded configurations. i use reference counts to deal
63  * w/ multiple configs sharing the same afp_options. oh, to dream of
64  * garbage collection ... */
65 void configfree(AFPConfig *configs, const AFPConfig *config)
66 {
67     AFPConfig *p, *q;
68
69     for (p = configs; p; p = q) {
70         q = p->next;
71         if (p == config)
72             continue;
73
74         /* do a little reference counting */
75         if (--(*p->optcount) < 1) {
76             afp_options_free(&p->obj.options, p->defoptions);
77             free(p->optcount);
78         }
79
80         switch (p->obj.proto) {
81 #ifndef NO_DDP
82         case AFPPROTO_ASP:
83             free(p->obj.Obj);
84             free(p->obj.Type);
85             free(p->obj.Zone);
86             atp_close(((ASP) p->obj.handle)->asp_atp);
87             free(p->obj.handle);
88             break;
89 #endif /* no afp/asp */
90         case AFPPROTO_DSI:
91             close(p->fd);
92             free(p->obj.handle);
93             break;
94         }
95         free(p);
96     }
97 }
98
99 #ifdef USE_SRVLOC
100 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
101     *(SLPError*)cookie = errcode;
102 }
103
104 static char hex[17] = "0123456789abcdef";
105
106 static char * srvloc_encode(const struct afp_options *options, const char *name)
107 {
108         static char buf[512];
109         char *conv_name;
110         unsigned char *p;
111         unsigned int i = 0;
112 #ifndef NO_DDP
113         char *Obj, *Type = "", *Zone = "";
114 #endif
115
116         /* Convert name to maccharset */
117         if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
118                          name, -1, &conv_name)) )
119                 return (char*)name;
120
121         /* Escape characters */
122         p = conv_name;
123         while (*p && i<(sizeof(buf)-4)) {
124             if (*p == '@')
125                 break;
126             else if (isspace(*p)) {
127                 buf[i++] = '%';
128                 buf[i++] = '2';
129                 buf[i++] = '0';
130                 p++;
131             }   
132             else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
133                 buf[i++] = '%';
134                 buf[i++] = hex[*p >> 4];
135                 buf[i++] = hex[*p++ & 15];
136             }
137             else {
138                 buf[i++] = *p++;
139             }
140         }
141         buf[i] = '\0';
142
143 #ifndef NO_DDP
144         /* Add ZONE,  */
145         if (nbp_name(options->server, &Obj, &Type, &Zone )) {
146                 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
147         }
148         else {
149                 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
150         }
151 #endif
152         free (conv_name);
153
154         return buf;
155 }
156 #endif /* USE_SRVLOC */
157 #ifdef USE_ZEROCONF
158 #include "afp_zeroconf.h"
159 #endif /* USE_ZEROCONF */
160
161 static void dsi_cleanup(const AFPConfig *config)
162 {
163 #ifdef USE_SRVLOC
164     SLPError err;
165     SLPError callbackerr;
166     SLPHandle hslp;
167     DSI *dsi = (DSI *)config->obj.handle;
168
169     /*  Do nothing if we didn't register.  */
170     if (!dsi || dsi->srvloc_url[0] == '\0')
171         return;
172
173     err = SLPOpen("en", SLP_FALSE, &hslp);
174     if (err != SLP_OK) {
175         LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
176         goto srvloc_dereg_err;
177     }
178
179     err = SLPDereg(hslp,
180                    dsi->srvloc_url,
181                    SRVLOC_callback,
182                    &callbackerr);
183     if (err != SLP_OK) {
184         LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
185         goto srvloc_dereg_err;
186     }
187
188     if (callbackerr != SLP_OK) {
189         LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
190         goto srvloc_dereg_err;
191     }
192
193 srvloc_dereg_err:
194     dsi->srvloc_url[0] = '\0';
195     SLPClose(hslp);
196 #elif defined (USE_ZEROCONF)
197     DSI *dsi = (DSI *)config->obj.handle;
198
199     /*  Do nothing if we didn't register.  */
200     if (!dsi || dsi->zeroconf_registered == 0)
201         return;
202
203     zeroconf_deregister();
204 #endif /* USE_SRVLOC */
205 }
206
207 #ifndef NO_DDP
208 static void asp_cleanup(const AFPConfig *config)
209 {
210     /* we need to stop tickle handler */
211     asp_stop_tickle();
212     nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
213                 &config->obj.options.ddpaddr);
214 }
215
216 /* these two are almost identical. it should be possible to collapse them
217  * into one with minimal junk. */
218 static int asp_start(AFPConfig *config, AFPConfig *configs,
219                      server_child *server_children)
220 {
221     ASP asp;
222
223     if (!(asp = asp_getsession(config->obj.handle, server_children,
224                                config->obj.options.tickleval))) {
225         LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
226         exit( EXITERR_CLNT );
227     }
228
229     if (asp->child) {
230         configfree(configs, config); /* free a bunch of stuff */
231         afp_over_asp(&config->obj);
232         exit (0);
233     }
234
235     return 0;
236 }
237 #endif /* no afp/asp */
238
239 static int dsi_start(AFPConfig *config, AFPConfig *configs,
240                      server_child *server_children)
241 {
242     DSI *dsi;
243
244     if (!(dsi = dsi_getsession(config->obj.handle, server_children,
245                                config->obj.options.tickleval))) {
246         LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
247         exit( EXITERR_CLNT );
248     }
249
250     /* we've forked. */
251     if (dsi->child) {
252         configfree(configs, config);
253         afp_over_dsi(&config->obj); /* start a session */
254         exit (0);
255     }
256
257     return 0;
258 }
259
260 #ifndef NO_DDP
261 static AFPConfig *ASPConfigInit(const struct afp_options *options,
262                                 unsigned char *refcount)
263 {
264     AFPConfig *config;
265     ATP atp;
266     ASP asp;
267     char *Obj, *Type = "AFPServer", *Zone = "*";
268     char *convname = NULL;
269
270     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
271         return NULL;
272
273     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
274         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
275         free(config);
276         return NULL;
277     }
278
279     if ((asp = asp_init( atp )) == NULL) {
280         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
281         atp_close(atp);
282         free(config);
283         return NULL;
284     }
285
286     /* register asp server */
287     Obj = (char *) options->hostname;
288     if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
289                          options->server, strlen(options->server), &convname)) ) {
290         if ((convname = strdup(options->server)) == NULL ) {
291             LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
292             goto serv_free_return;
293         }
294     }
295
296     if (nbp_name(convname, &Obj, &Type, &Zone )) {
297         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
298         goto serv_free_return;
299     }
300     if (convname)
301         free (convname);
302
303     /* dup Obj, Type and Zone as they get assigned to a single internal
304      * buffer by nbp_name */
305     if ((config->obj.Obj  = strdup(Obj)) == NULL)
306         goto serv_free_return;
307
308     if ((config->obj.Type = strdup(Type)) == NULL) {
309         free(config->obj.Obj);
310         goto serv_free_return;
311     }
312
313     if ((config->obj.Zone = strdup(Zone)) == NULL) {
314         free(config->obj.Obj);
315         free(config->obj.Type);
316         goto serv_free_return;
317     }
318
319     /* make sure we're not registered */
320     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
321     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
322         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
323         free(config->obj.Obj);
324         free(config->obj.Type);
325         free(config->obj.Zone);
326         goto serv_free_return;
327     }
328
329     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
330         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
331         atp_sockaddr( atp )->sat_addr.s_node,
332         atp_sockaddr( atp )->sat_port, VERSION );
333
334     config->fd = atp_fileno(atp);
335     config->obj.handle = asp;
336     config->obj.config = config;
337     config->obj.proto = AFPPROTO_ASP;
338
339     memcpy(&config->obj.options, options, sizeof(struct afp_options));
340     config->optcount = refcount;
341     (*refcount)++;
342
343     config->server_start = asp_start;
344     config->server_cleanup = asp_cleanup;
345
346     return config;
347
348 serv_free_return:
349                     asp_close(asp);
350     free(config);
351     return NULL;
352 }
353 #endif /* no afp/asp */
354
355
356 static AFPConfig *DSIConfigInit(const struct afp_options *options,
357                                 unsigned char *refcount,
358                                 const dsi_proto protocol)
359 {
360     AFPConfig *config;
361     DSI *dsi;
362     char *p, *q;
363
364     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
365         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
366         return NULL;
367     }
368
369     LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
370         options->hostname,
371         options->ipaddr ? options->ipaddr : "default",
372         options->port ? options->port : "548");
373
374     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
375                         options->ipaddr, options->port,
376                         options->flags & OPTION_PROXY,
377                         options->server_quantum)) == NULL) {
378         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
379         free(config);
380         return NULL;
381     }
382
383     if (options->flags & OPTION_PROXY) {
384         LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
385             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
386     } else {
387         LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
388             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
389     }
390
391 #ifdef USE_SRVLOC
392     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
393     if (!(options->flags & OPTION_NOSLP)) {
394         SLPError err;
395         SLPError callbackerr;
396         SLPHandle hslp;
397         unsigned int afp_port;
398         int   l;
399         char *srvloc_hostname;
400         const char *hostname;
401
402         err = SLPOpen("en", SLP_FALSE, &hslp);
403         if (err != SLP_OK) {
404             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
405             goto srvloc_reg_err;
406         }
407
408         /* XXX We don't want to tack on the port number if we don't have to.
409          * Why?
410          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
411          * use a non-default port, they can, but be aware, this server might
412          * not show up int the Network Browser.
413          */
414         afp_port = getip_port((struct sockaddr *)&dsi->server);
415         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
416         p = NULL;
417         if (options->fqdn) {
418             hostname = options->fqdn;
419             p = strchr(hostname, ':');
420         }       
421         else 
422             hostname = getip_string((struct sockaddr *)&dsi->server);
423
424         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
425
426         if ((p) || afp_port == 548) {
427             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
428         }
429         else {
430             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
431         }
432
433         if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
434             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
435             dsi->srvloc_url[0] = '\0';
436             goto srvloc_reg_err;
437         }
438
439         err = SLPReg(hslp,
440                      dsi->srvloc_url,
441                      SLP_LIFETIME_MAXIMUM,
442                      "afp",
443                      "",
444                      SLP_TRUE,
445                      SRVLOC_callback,
446                      &callbackerr);
447         if (err != SLP_OK) {
448             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
449             dsi->srvloc_url[0] = '\0';
450             goto srvloc_reg_err;
451         }
452
453         if (callbackerr != SLP_OK) {
454             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
455             dsi->srvloc_url[0] = '\0';
456             goto srvloc_reg_err;
457         }
458
459         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
460         config->server_cleanup = dsi_cleanup;
461
462 srvloc_reg_err:
463         SLPClose(hslp);
464     }
465 #endif /* USE_SRVLOC */
466
467 #ifdef USE_ZEROCONF
468     uint afp_port;
469     const char *hostname = NULL;
470
471     dsi->zeroconf_registered = 0; /*  Mark that we haven't registered.  */
472
473     if (!(options->flags & OPTION_NOZEROCONF)) {
474         /* XXX We don't want to tack on the port number if we don't have to.
475            * Why?
476            * Well, this seems to break MacOS < 10.  If the user _really_ wants to
477            * use a non-default port, they can, but be aware, this server might
478            * not show up int the Network Browser.
479            */
480         afp_port = getip_port((struct sockaddr *)&dsi->server);
481
482         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
483         p = NULL;
484         if (options->fqdn) {
485             hostname = options->fqdn;
486             p = strchr(hostname, ':');
487         }  else {
488             hostname = getip_string((struct sockaddr *)&dsi->server);
489         }
490
491         zeroconf_register(afp_port, hostname);
492         dsi->zeroconf_registered = 1; /*  Mark that we have registered.  */
493         config->server_cleanup = dsi_cleanup;
494     }
495 #endif /* USE_ZEROCONF */
496
497     config->fd = dsi->serversock;
498     config->obj.handle = dsi;
499     config->obj.config = config;
500     config->obj.proto = AFPPROTO_DSI;
501
502     memcpy(&config->obj.options, options, sizeof(struct afp_options));
503     /* get rid of any appletalk info. we use the fact that the DSI
504      * stuff is done after the ASP stuff. */
505     p = config->obj.options.server;
506     if (p && (q = strchr(p, ':')))
507         *q = '\0';
508
509     config->optcount = refcount;
510     (*refcount)++;
511
512     config->server_start = dsi_start;
513     return config;
514 }
515
516 /* allocate server configurations. this should really store the last
517  * entry in config->last or something like that. that would make
518  * supporting multiple dsi transports easier. */
519 static AFPConfig *AFPConfigInit(const struct afp_options *options,
520                                 const struct afp_options *defoptions)
521 {
522     AFPConfig *config = NULL, *next = NULL;
523     unsigned char *refcount;
524
525     if ((refcount = (unsigned char *)
526                     calloc(1, sizeof(unsigned char))) == NULL) {
527         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
528         return NULL;
529     }
530
531 #ifndef NO_DDP
532     /* handle asp transports */
533     if ((options->transports & AFPTRANS_DDP) &&
534             (config = ASPConfigInit(options, refcount)))
535         config->defoptions = defoptions;
536 #endif /* NO_DDP */
537
538
539     /* set signature */
540     set_signature(options);
541
542     /* handle dsi transports and dsi proxies. we only proxy
543      * for DSI connections. */
544
545     /* this should have something like the following:
546      * for (i=mindsi; i < maxdsi; i++)
547      *   if (options->transports & (1 << i) && 
548      *     (next = DSIConfigInit(options, refcount, i)))
549      *     next->defoptions = defoptions;
550      */
551     if ((options->transports & AFPTRANS_TCP) &&
552             (((options->flags & OPTION_PROXY) == 0) ||
553              ((options->flags & OPTION_PROXY) && config))
554             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
555         next->defoptions = defoptions;
556
557     /* load in all the authentication modules. we can load the same
558        things multiple times if necessary. however, loading different
559        things with the same names will cause complaints. by not loading
560        in any uams with proxies, we prevent ddp connections from succeeding.
561     */
562     auth_load(options->uampath, options->uamlist);
563
564     /* this should be able to accept multiple dsi transports. i think
565      * the only thing that gets affected is the net addresses. */
566     status_init(config, next, options);
567
568     /* attach dsi config to tail of asp config */
569     if (config) {
570         config->next = next;
571         return config;
572     }
573
574     return next;
575 }
576
577 /* fill in the appropriate bits for each interface */
578 AFPConfig *configinit(struct afp_options *cmdline)
579 {
580     FILE *fp;
581     char buf[LINESIZE + 1], *p, have_option = 0;
582     size_t len;
583     struct afp_options options;
584     AFPConfig *config=NULL, *first = NULL; 
585
586 #ifdef HAVE_ACLS
587     /* Parse afp_ldap.conf first so we can set the uuid option */
588     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
589 #endif /* HAVE_ACLS */
590
591     /* if config file doesn't exist, load defaults */
592     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
593     {
594         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
595             cmdline->configfile);
596         return AFPConfigInit(cmdline, cmdline);
597     }
598
599     /* scan in the configuration file */
600     len = 0;
601     while (!feof(fp)) {
602         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
603             continue;
604         len = strlen(buf);
605         if ( len >= 2 && buf[len-2] == '\\' ) {
606             len -= 2;
607             continue;
608         } else
609             len = 0;
610
611         /* a little pre-processing to get rid of spaces and end-of-lines */
612         p = buf;
613         while (p && isspace(*p))
614             p++;
615         if (!p || (*p == '\0'))
616             continue;
617
618         have_option = 1;
619
620         memcpy(&options, cmdline, sizeof(options));
621         if (!afp_options_parseline(p, &options))
622             continue;
623
624 #ifdef HAVE_ACLS
625         /* Enable UUID support if LDAP config is complete */
626         if (ldap_config_valid) {
627             LOG(log_info, logtype_afpd, "Enabling UUID support");
628             options.flags |= OPTION_UUID;
629         }
630 #endif /* HAVE_ACLS */
631
632         /* this should really get a head and a tail to simplify things. */
633         if (!first) {
634             if ((first = AFPConfigInit(&options, cmdline)))
635                 config = first->next ? first->next : first;
636         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
637             config = config->next->next ? config->next->next : config->next;
638         }
639     }
640
641     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
642     fclose(fp);
643
644     if (!have_option)
645         first = AFPConfigInit(cmdline, cmdline);
646
647     return first;
648 }