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