]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
f69c74a854ba58dc81a76c75e7c55cc4fbb9706b
[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     dsi->zeroconf_registered = 0; /*  Mark that we haven't registered.  */
469     if (!(options->flags & OPTION_NOZEROCONF)) {
470         zeroconf_register(getip_port((struct sockaddr *)&dsi->server),
471                           options->server ? options->server : options->hostname);
472         dsi->zeroconf_registered = 1; /*  Mark that we have registered.  */
473         config->server_cleanup = dsi_cleanup;
474     }
475 #endif /* USE_ZEROCONF */
476
477     config->fd = dsi->serversock;
478     config->obj.handle = dsi;
479     config->obj.config = config;
480     config->obj.proto = AFPPROTO_DSI;
481
482     memcpy(&config->obj.options, options, sizeof(struct afp_options));
483     /* get rid of any appletalk info. we use the fact that the DSI
484      * stuff is done after the ASP stuff. */
485     p = config->obj.options.server;
486     if (p && (q = strchr(p, ':')))
487         *q = '\0';
488
489     config->optcount = refcount;
490     (*refcount)++;
491
492     config->server_start = dsi_start;
493     return config;
494 }
495
496 /* allocate server configurations. this should really store the last
497  * entry in config->last or something like that. that would make
498  * supporting multiple dsi transports easier. */
499 static AFPConfig *AFPConfigInit(const struct afp_options *options,
500                                 const struct afp_options *defoptions)
501 {
502     AFPConfig *config = NULL, *next = NULL;
503     unsigned char *refcount;
504
505     if ((refcount = (unsigned char *)
506                     calloc(1, sizeof(unsigned char))) == NULL) {
507         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
508         return NULL;
509     }
510
511 #ifndef NO_DDP
512     /* handle asp transports */
513     if ((options->transports & AFPTRANS_DDP) &&
514             (config = ASPConfigInit(options, refcount)))
515         config->defoptions = defoptions;
516 #endif /* NO_DDP */
517
518
519     /* set signature */
520     set_signature(options);
521
522     /* handle dsi transports and dsi proxies. we only proxy
523      * for DSI connections. */
524
525     /* this should have something like the following:
526      * for (i=mindsi; i < maxdsi; i++)
527      *   if (options->transports & (1 << i) && 
528      *     (next = DSIConfigInit(options, refcount, i)))
529      *     next->defoptions = defoptions;
530      */
531     if ((options->transports & AFPTRANS_TCP) &&
532             (((options->flags & OPTION_PROXY) == 0) ||
533              ((options->flags & OPTION_PROXY) && config))
534             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
535         next->defoptions = defoptions;
536
537     /* load in all the authentication modules. we can load the same
538        things multiple times if necessary. however, loading different
539        things with the same names will cause complaints. by not loading
540        in any uams with proxies, we prevent ddp connections from succeeding.
541     */
542     auth_load(options->uampath, options->uamlist);
543
544     /* this should be able to accept multiple dsi transports. i think
545      * the only thing that gets affected is the net addresses. */
546     status_init(config, next, options);
547
548     /* attach dsi config to tail of asp config */
549     if (config) {
550         config->next = next;
551         return config;
552     }
553
554     return next;
555 }
556
557 /* fill in the appropriate bits for each interface */
558 AFPConfig *configinit(struct afp_options *cmdline)
559 {
560     FILE *fp;
561     char buf[LINESIZE + 1], *p, have_option = 0;
562     size_t len;
563     struct afp_options options;
564     AFPConfig *config=NULL, *first = NULL; 
565
566 #ifdef HAVE_ACLS
567     /* Parse afp_ldap.conf first so we can set the uuid option */
568     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
569 #endif /* HAVE_ACLS */
570
571     /* if config file doesn't exist, load defaults */
572     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
573     {
574         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
575             cmdline->configfile);
576         return AFPConfigInit(cmdline, cmdline);
577     }
578
579     /* scan in the configuration file */
580     len = 0;
581     while (!feof(fp)) {
582         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
583             continue;
584         len = strlen(buf);
585         if ( len >= 2 && buf[len-2] == '\\' ) {
586             len -= 2;
587             continue;
588         } else
589             len = 0;
590
591         /* a little pre-processing to get rid of spaces and end-of-lines */
592         p = buf;
593         while (p && isspace(*p))
594             p++;
595         if (!p || (*p == '\0'))
596             continue;
597
598         have_option = 1;
599
600         memcpy(&options, cmdline, sizeof(options));
601         if (!afp_options_parseline(p, &options))
602             continue;
603
604 #ifdef HAVE_ACLS
605         /* Enable UUID support if LDAP config is complete */
606         if (ldap_config_valid) {
607             LOG(log_info, logtype_afpd, "Enabling UUID support");
608             options.flags |= OPTION_UUID;
609         }
610 #endif /* HAVE_ACLS */
611
612         /* this should really get a head and a tail to simplify things. */
613         if (!first) {
614             if ((first = AFPConfigInit(&options, cmdline)))
615                 config = first->next ? first->next : first;
616         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
617             config = config->next->next ? config->next->next : config->next;
618         }
619     }
620
621     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
622     fclose(fp);
623
624     if (!have_option)
625         first = AFPConfigInit(cmdline, cmdline);
626
627     return first;
628 }