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 <atalk/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 ... */ 51void 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 89static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) { 90 *(SLPError*)cookie = errcode; 91} 92 93static char hex[17] = "0123456789abcdef"; 94 95static 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 147static 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 179srvloc_dereg_err: 180 dsi->srvloc_url[0] = '\0'; 181 SLPClose(hslp); 182#endif /* USE_SRVLOC */ 183} 184 185#ifndef NO_DDP 186static 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. */ 196static 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 217static afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs, 218 server_child *server_children) 219{ 220 DSI *dsi = config->obj.handle; 221 afp_child_t *child = NULL; 222 223 if (!(child = dsi_getsession(dsi, 224 server_children, 225 config->obj.options.tickleval))) { 226 /* we've forked. */ 227 configfree(configs, config); 228 afp_over_dsi(&config->obj); /* start a session */ 229 exit (0); 230 } 231 232 return child; 233} 234 235#ifndef NO_DDP 236static AFPConfig *ASPConfigInit(const struct afp_options *options, 237 unsigned char *refcount) 238{ 239 AFPConfig *config; 240 ATP atp; 241 ASP asp; 242 char *Obj, *Type = "AFPServer", *Zone = "*"; 243 char *convname = NULL; 244 245 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) 246 return NULL; 247 248 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) { 249 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) ); 250 free(config); 251 return NULL; 252 } 253 254 if ((asp = asp_init( atp )) == NULL) { 255 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) ); 256 atp_close(atp); 257 free(config); 258 return NULL; 259 } 260 261 /* register asp server */ 262 Obj = (char *) options->hostname; 263 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset, 264 options->server, strlen(options->server), &convname)) ) { 265 if ((convname = strdup(options->server)) == NULL ) { 266 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) ); 267 goto serv_free_return; 268 } 269 } 270 271 if (nbp_name(convname, &Obj, &Type, &Zone )) { 272 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server ); 273 goto serv_free_return; 274 } 275 if (convname) 276 free (convname); 277 278 /* dup Obj, Type and Zone as they get assigned to a single internal 279 * buffer by nbp_name */ 280 if ((config->obj.Obj = strdup(Obj)) == NULL) 281 goto serv_free_return; 282 283 if ((config->obj.Type = strdup(Type)) == NULL) { 284 free(config->obj.Obj); 285 goto serv_free_return; 286 } 287 288 if ((config->obj.Zone = strdup(Zone)) == NULL) { 289 free(config->obj.Obj); 290 free(config->obj.Type); 291 goto serv_free_return; 292 } 293 294 /* make sure we're not registered */ 295 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr); 296 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) { 297 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone ); 298 free(config->obj.Obj); 299 free(config->obj.Type); 300 free(config->obj.Zone); 301 goto serv_free_return; 302 } 303 304 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone, 305 ntohs( atp_sockaddr( atp )->sat_addr.s_net ), 306 atp_sockaddr( atp )->sat_addr.s_node, 307 atp_sockaddr( atp )->sat_port, VERSION ); 308 309 config->fd = atp_fileno(atp); 310 config->obj.handle = asp; 311 config->obj.config = config; 312 config->obj.proto = AFPPROTO_ASP; 313 314 memcpy(&config->obj.options, options, sizeof(struct afp_options)); 315 config->optcount = refcount; 316 (*refcount)++; 317 318 config->server_start = asp_start; 319 config->server_cleanup = asp_cleanup; 320 321 return config; 322 323serv_free_return: 324 asp_close(asp); 325 free(config); 326 return NULL; 327} 328#endif /* no afp/asp */ 329 330 331static AFPConfig *DSIConfigInit(const struct afp_options *options, 332 unsigned char *refcount, 333 const dsi_proto protocol) 334{ 335 AFPConfig *config; 336 DSI *dsi; 337 char *p, *q; 338 339 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) { 340 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) ); 341 return NULL; 342 } 343 344 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ", 345 options->hostname, 346 options->ipaddr ? options->ipaddr : "default", 347 options->port ? options->port : "548"); 348 349 if ((dsi = dsi_init(protocol, "afpd", options->hostname, 350 options->ipaddr, options->port, 351 options->flags & OPTION_PROXY, 352 options->server_quantum)) == NULL) { 353 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) ); 354 free(config); 355 return NULL; 356 } 357 dsi->dsireadbuf = options->dsireadbuf; 358 359 if (options->flags & OPTION_PROXY) { 360 LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)", 361 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION); 362 } else { 363 LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)", 364 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION); 365 } 366 367#ifdef USE_SRVLOC 368 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */ 369 if (!(options->flags & OPTION_NOSLP)) { 370 SLPError err; 371 SLPError callbackerr; 372 SLPHandle hslp; 373 unsigned int afp_port; 374 int l; 375 char *srvloc_hostname; 376 const char *hostname; 377 378 err = SLPOpen("en", SLP_FALSE, &hslp); 379 if (err != SLP_OK) { 380 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle"); 381 goto srvloc_reg_err; 382 } 383 384 /* XXX We don't want to tack on the port number if we don't have to. 385 * Why? 386 * Well, this seems to break MacOS < 10. If the user _really_ wants to 387 * use a non-default port, they can, but be aware, this server might 388 * not show up int the Network Browser. 389 */ 390 afp_port = getip_port((struct sockaddr *)&dsi->server); 391 /* If specified use the FQDN to register with srvloc, otherwise use IP. */ 392 p = NULL; 393 if (options->fqdn) { 394 hostname = options->fqdn; 395 p = strchr(hostname, ':'); 396 } 397 else 398 hostname = getip_string((struct sockaddr *)&dsi->server); 399 400 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname)); 401 402 if ((p) || afp_port == 548) { 403 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname); 404 } 405 else { 406 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname); 407 } 408 409 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) { 410 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC"); 411 dsi->srvloc_url[0] = '\0'; 412 goto srvloc_reg_err; 413 } 414 415 err = SLPReg(hslp, 416 dsi->srvloc_url, 417 SLP_LIFETIME_MAXIMUM, 418 "afp", 419 "", 420 SLP_TRUE, 421 SRVLOC_callback, 422 &callbackerr); 423 if (err != SLP_OK) { 424 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url); 425 dsi->srvloc_url[0] = '\0'; 426 goto srvloc_reg_err; 427 } 428 429 if (callbackerr != SLP_OK) { 430 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url); 431 dsi->srvloc_url[0] = '\0'; 432 goto srvloc_reg_err; 433 } 434 435 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url); 436 config->server_cleanup = dsi_cleanup; 437 438srvloc_reg_err: 439 SLPClose(hslp); 440 } 441#endif /* USE_SRVLOC */ 442 443 config->fd = dsi->serversock; 444 config->obj.handle = dsi; 445 dsi->AFPobj = &config->obj; 446 config->obj.config = config; 447 config->obj.proto = AFPPROTO_DSI; 448 449 memcpy(&config->obj.options, options, sizeof(struct afp_options)); 450 /* get rid of any appletalk info. we use the fact that the DSI 451 * stuff is done after the ASP stuff. */ 452 p = config->obj.options.server; 453 if (p && (q = strchr(p, ':'))) 454 *q = '\0'; 455 456 config->optcount = refcount; 457 (*refcount)++; 458 459 config->server_start = dsi_start; 460 return config; 461} 462 463/* allocate server configurations. this should really store the last 464 * entry in config->last or something like that. that would make 465 * supporting multiple dsi transports easier. */ 466static AFPConfig *AFPConfigInit(struct afp_options *options, 467 const struct afp_options *defoptions) 468{ 469 AFPConfig *config = NULL, *next = NULL; 470 unsigned char *refcount; 471 472 if ((refcount = (unsigned char *) 473 calloc(1, sizeof(unsigned char))) == NULL) { 474 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) ); 475 return NULL; 476 } 477 478#ifndef NO_DDP 479 /* handle asp transports */ 480 if ((options->transports & AFPTRANS_DDP) && 481 (config = ASPConfigInit(options, refcount))) 482 config->defoptions = defoptions; 483#endif /* NO_DDP */ 484 485 486 /* set signature */ 487 set_signature(options); 488 489 /* handle dsi transports and dsi proxies. we only proxy 490 * for DSI connections. */ 491 492 /* this should have something like the following: 493 * for (i=mindsi; i < maxdsi; i++) 494 * if (options->transports & (1 << i) && 495 * (next = DSIConfigInit(options, refcount, i))) 496 * next->defoptions = defoptions; 497 */ 498 if ((options->transports & AFPTRANS_TCP) && 499 (((options->flags & OPTION_PROXY) == 0) || 500 ((options->flags & OPTION_PROXY) && config)) 501 && (next = DSIConfigInit(options, refcount, DSI_TCPIP))) 502 next->defoptions = defoptions; 503 504 /* load in all the authentication modules. we can load the same 505 things multiple times if necessary. however, loading different 506 things with the same names will cause complaints. by not loading 507 in any uams with proxies, we prevent ddp connections from succeeding. 508 */ 509 auth_load(options->uampath, options->uamlist); 510 511 /* this should be able to accept multiple dsi transports. i think 512 * the only thing that gets affected is the net addresses. */ 513 status_init(config, next, options); 514 515 /* attach dsi config to tail of asp config */ 516 if (config) { 517 config->next = next; 518 return config; 519 } 520 521 return next; 522} 523 524/* fill in the appropriate bits for each interface */ 525AFPConfig *configinit(struct afp_options *cmdline) 526{ 527 FILE *fp; 528 char buf[LINESIZE + 1], *p, have_option = 0; 529 size_t len; 530 struct afp_options options; 531 AFPConfig *config=NULL, *first = NULL; 532 533 /* if config file doesn't exist, load defaults */ 534 if ((fp = fopen(cmdline->configfile, "r")) == NULL) 535 { 536 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults", 537 cmdline->configfile); 538 return AFPConfigInit(cmdline, cmdline); 539 } 540 541 /* scan in the configuration file */ 542 len = 0; 543 while (!feof(fp)) { 544 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#') 545 continue; 546 len = strlen(buf); 547 if ( len >= 2 && buf[len-2] == '\\' ) { 548 len -= 2; 549 continue; 550 } else 551 len = 0; 552 553 /* a little pre-processing to get rid of spaces and end-of-lines */ 554 p = buf; 555 while (p && isspace(*p)) 556 p++; 557 if (!p || (*p == '\0')) 558 continue; 559 560 have_option = 1; 561 562 memcpy(&options, cmdline, sizeof(options)); 563 if (!afp_options_parseline(p, &options)) 564 continue; 565 566 /* AFPConfigInit can return two linked configs due to DSI and ASP */ 567 if (!first) { 568 if ((first = AFPConfigInit(&options, cmdline))) 569 config = first->next ? first->next : first; 570 } else if ((config->next = AFPConfigInit(&options, cmdline))) { 571 config = config->next->next ? config->next->next : config->next; 572 } 573 } 574 575#ifdef HAVE_LDAP 576 /* Parse afp_ldap.conf */ 577 acl_ldap_readconfig(_PATH_ACL_LDAPCONF); 578#endif /* HAVE_LDAP */ 579 580 LOG(log_debug, logtype_afpd, "Finished parsing Config File"); 581 fclose(fp); 582 583 if (!have_option) 584 first = AFPConfigInit(cmdline, cmdline); 585 586 /* Now register with zeroconf, we also need the volumes for that */ 587 if (first && !(first->obj.options.flags & OPTION_NOZEROCONF)) { 588 load_volumes(&first->obj); 589 zeroconf_register(first); 590 } 591 592 return first; 593} 594