]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
compute the right size if len == -1 in convert_string and use -1 than strlen(in)...
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * $Id: afp_config.c,v 1.28 2009-10-29 11:35:58 didg 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     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
369                         options->ipaddr, options->port,
370                         options->flags & OPTION_PROXY,
371                         options->server_quantum)) == NULL) {
372         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
373         free(config);
374         return NULL;
375     }
376
377     if (options->flags & OPTION_PROXY) {
378         LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
379             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
380             VERSION);
381     } else {
382         LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
383             inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
384             dsi->serversock, VERSION);
385     }
386
387 #ifdef USE_SRVLOC
388     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
389     if (!(options->flags & OPTION_NOSLP)) {
390         err = SLPOpen("en", SLP_FALSE, &hslp);
391         if (err != SLP_OK) {
392             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
393             goto srvloc_reg_err;
394         }
395
396         /* XXX We don't want to tack on the port number if we don't have to.
397          * Why?
398          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
399          * use a non-default port, they can, but be aware, this server might
400          * not show up int the Network Browser.
401          */
402         afpovertcp = getservbyname("afpovertcp", "tcp");
403         if (afpovertcp != NULL) {
404             afp_port = afpovertcp->s_port;
405         }
406         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
407         p = NULL;
408         if (options->fqdn) {
409             hostname = options->fqdn;
410             p = strchr(hostname, ':');
411         }       
412         else 
413             hostname = inet_ntoa(dsi->server.sin_addr);
414         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
415
416         if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
417             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
418             dsi->srvloc_url[0] = '\0';
419             goto srvloc_reg_err;
420         }
421         if ((p) || dsi->server.sin_port == afp_port) {
422             sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
423         }
424         else {
425             sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
426         }
427
428         err = SLPReg(hslp,
429                      dsi->srvloc_url,
430                      SLP_LIFETIME_MAXIMUM,
431                      "afp",
432                      "",
433                      SLP_TRUE,
434                      SRVLOC_callback,
435                      &callbackerr);
436         if (err != SLP_OK) {
437             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
438             dsi->srvloc_url[0] = '\0';
439             goto srvloc_reg_err;
440         }
441
442         if (callbackerr != SLP_OK) {
443             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
444             dsi->srvloc_url[0] = '\0';
445             goto srvloc_reg_err;
446         }
447
448         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
449
450 srvloc_reg_err:
451         SLPClose(hslp);
452     }
453 #endif /* USE_SRVLOC */
454
455
456     config->fd = dsi->serversock;
457     config->obj.handle = dsi;
458     config->obj.config = config;
459     config->obj.proto = AFPPROTO_DSI;
460
461     memcpy(&config->obj.options, options, sizeof(struct afp_options));
462     /* get rid of any appletalk info. we use the fact that the DSI
463      * stuff is done after the ASP stuff. */
464     p = config->obj.options.server;
465     if (p && (q = strchr(p, ':')))
466         *q = '\0';
467
468     config->optcount = refcount;
469     (*refcount)++;
470
471     config->server_start = dsi_start;
472 #ifdef USE_SRVLOC
473     config->server_cleanup = dsi_cleanup;
474 #endif 
475     return config;
476 }
477
478 /* allocate server configurations. this should really store the last
479  * entry in config->last or something like that. that would make
480  * supporting multiple dsi transports easier. */
481 static AFPConfig *AFPConfigInit(const struct afp_options *options,
482                                 const struct afp_options *defoptions)
483 {
484     AFPConfig *config = NULL, *next = NULL;
485     unsigned char *refcount;
486
487     if ((refcount = (unsigned char *)
488                     calloc(1, sizeof(unsigned char))) == NULL) {
489         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
490         return NULL;
491     }
492
493 #ifndef NO_DDP
494     /* handle asp transports */
495     if ((options->transports & AFPTRANS_DDP) &&
496             (config = ASPConfigInit(options, refcount)))
497         config->defoptions = defoptions;
498 #endif /* NO_DDP */
499
500     /* handle dsi transports and dsi proxies. we only proxy
501      * for DSI connections. */
502
503     /* this should have something like the following:
504      * for (i=mindsi; i < maxdsi; i++)
505      *   if (options->transports & (1 << i) && 
506      *     (next = DSIConfigInit(options, refcount, i)))
507      *     next->defoptions = defoptions;
508      */
509     if ((options->transports & AFPTRANS_TCP) &&
510             (((options->flags & OPTION_PROXY) == 0) ||
511              ((options->flags & OPTION_PROXY) && config))
512             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
513         next->defoptions = defoptions;
514
515     /* load in all the authentication modules. we can load the same
516        things multiple times if necessary. however, loading different
517        things with the same names will cause complaints. by not loading
518        in any uams with proxies, we prevent ddp connections from succeeding.
519     */
520     auth_load(options->uampath, options->uamlist);
521
522     /* this should be able to accept multiple dsi transports. i think
523      * the only thing that gets affected is the net addresses. */
524     status_init(config, next, options);
525
526     /* attach dsi config to tail of asp config */
527     if (config) {
528         config->next = next;
529         return config;
530     }
531
532     return next;
533 }
534
535 /* fill in the appropriate bits for each interface */
536 AFPConfig *configinit(struct afp_options *cmdline)
537 {
538     FILE *fp;
539     char buf[LINESIZE + 1], *p, have_option = 0;
540     size_t len;
541     struct afp_options options;
542     AFPConfig *config=NULL, *first = NULL; 
543
544 #ifdef HAVE_NFSv4_ACLS
545     /* Parse ldap.conf first so we can set the uuid option */
546     LOG(log_debug, logtype_afpd, "Start parsing ldap.conf");
547     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
548     LOG(log_debug, logtype_afpd, "Finished parsing ldap.conf");
549 #endif
550
551     status_reset();
552     /* if config file doesn't exist, load defaults */
553     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
554     {
555         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
556             cmdline->configfile);
557         return AFPConfigInit(cmdline, cmdline);
558     }
559
560     LOG(log_debug, logtype_afpd, "Loading ConfigFile"); 
561
562     /* scan in the configuration file */
563     len = 0;
564     while (!feof(fp)) {
565         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
566             continue;
567         len = strlen(buf);
568         if ( len >= 2 && buf[len-2] == '\\' ) {
569             len -= 2;
570             continue;
571         } else
572             len = 0;
573
574         /* a little pre-processing to get rid of spaces and end-of-lines */
575         p = buf;
576         while (p && isspace(*p))
577             p++;
578         if (!p || (*p == '\0'))
579             continue;
580
581         have_option = 1;
582
583         memcpy(&options, cmdline, sizeof(options));
584         if (!afp_options_parseline(p, &options))
585             continue;
586
587 #ifdef HAVE_NFSv4_ACLS
588         /* Enable UUID support if LDAP config is complete */
589         if (ldap_config_valid)
590             options.flags |= OPTION_UUID;
591 #endif
592
593         /* this should really get a head and a tail to simplify things. */
594         if (!first) {
595             if ((first = AFPConfigInit(&options, cmdline)))
596                 config = first->next ? first->next : first;
597         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
598             config = config->next->next ? config->next->next : config->next;
599         }
600     }
601
602     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
603     fclose(fp);
604
605     if (!have_option)
606         first = AFPConfigInit(cmdline, cmdline);
607
608     return first;
609 }