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