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