/*****************************************************************************\ * Copyright (c) 2002 Pelle Johansson. * * All rights reserved. * * * * This file is part of the moftpd package. Use and distribution of * * this software is governed by the terms in the file LICENCE, which * * should have come with this package. * * Notice that after passing this file through a yacc parser, you may * * also have to consider the licence of its output. * \*****************************************************************************/ /* $moftpd: config.y 1264 2005-04-06 13:32:27Z morth $ */ %{ #include "system.h" #include "confparse.h" #include "server.h" #include "user.h" #include "utf8fs/file.h" #include "utf8fs/memory.h" static struct stack { void *item; struct stack *next; } *servstack, *userstack, *dirstack, *servtop, *usertop, *dirtop; static server_t *currserv; static user_t *curruser; static access_t *currdir; int confExpectingToken; extern uid_t unprivUid; extern gid_t unprivGid; extern int xferBufferSize, doFakeChroot, forkConnections, selfFailedFork; extern size_t maxMmapSize; extern const char *localeDir, *pidFile, *accPath; extern char *sslCertsPath; %} %union { long long num; char str[1000]; } %token D_NUMBER %token D_STRING %token D_BOOL %token D_UID %token D_GID %token I_SERVER I_USER I_DIRECTORY %token I_UNPRIVUID I_UNPRIVGID I_XFERBUFFSIZE I_FAKECHROOT %token I_FORKCLIENTS I_RESET I_EXEC I_LOCALEDIR I_TRUSTEDCERTSDIR I_MAXMMAPSIZE %token I_PIDFILE I_ACCOUNTERSOCK I_LIMITFILESIZE I_UNLIMIT I_HANDLEFAILEDFORK %token I_PORT I_BIND I_MINPASVPORT I_MAXPASVPORT I_PASSIFINVALID I_ALLOWUNBOUND %token I_ALLOWFOREIGN I_ALLOWLOWPORTS I_USERALIAS I_SLEEPONFAIL I_ALLOWOUTOFRANGE %token I_MAXLOGINATTEMPTS I_PAMSERVICE I_RANGE I_DEFAULTHARDLINK %token I_ACCEPTTLS I_TLSVERIFYCLIENT I_TLSAUTOLOGIN I_TLSNONEWUSER %token I_DIRECTORYMSGFILE I_MAXCONNECTS %token I_SQLCONNECT I_SQLPASSWORD I_SQLTLSCONNECT I_SQLUSERQUERY I_SQLDIRQUERY %token I_SQLCONNECTQUERY %token I_LOGINFAILEDMSG I_USERINVALIDMSG I_PASSREQUESTMSG I_ANONPASSMSG %token I_WELCOMEMSG %token I_ANONYMOUS I_HOME I_CHROOT I_ALIAS I_PASSWORDNEEDED I_PASSWORD I_UID %token I_GID I_ALLOWLOGIN I_MAXIDLE I_ALLOWSECLOGIN I_MAXLOGINS %token I_EXTERNLOGIN I_EXTERNACCESS %token I_ADMIN %token I_LIST I_MSG I_ABORT I_DISCONNECT I_RELOAD %token I_ALLOW I_DENY I_HIDDEN I_FAKEUSER I_FAKEGROUP I_REQUIRE I_MASK %token I_SEARCH I_READFILE I_LISTING I_CREATEFILE I_CREATEDIR I_APPEND I_OVERWRITE %token I_FAKEFILEMODE I_FAKEDIRMODE I_HARDLINK %token I_DELETE I_RENAME I_READING I_WRITING I_STORING I_ALL I_ENCRYPTED I_SIGNED %token I_ETAG %type bool accesslist accessitem adminlist adminitem limit %% input: /* empty */ | input topst ; semicolon: /* empty */ | ';' ; bool: D_BOOL { $$ = $1; } | D_NUMBER { $$ = $1; } ; limit: I_UNLIMIT { $$ = RLIM_INFINITY; } | D_NUMBER { $$ = $1; } ; topst: topopt semicolon | server ; topopt: I_UNPRIVUID { confExpectingToken = D_UID; } D_UID { unprivUid = $3; } | I_UNPRIVGID { confExpectingToken = D_GID; } D_GID { unprivGid = $3; } | I_XFERBUFFSIZE D_NUMBER { if($2 <= 0) { syslog (LOG_ERR, "XferBuffSize must be positive."); YYABORT; } xferBufferSize = $2; } | I_FAKECHROOT bool { doFakeChroot = $2; } | I_FORKCLIENTS bool { forkConnections = $2; } | I_LOCALEDIR { confExpectingToken = D_STRING; } D_STRING { if ($3[strlen ($3) - 1] != '/') { char *tmp = talloc (strlen ($3) + 2); if (tmp) { strcpy (tmp, $3); strcat (tmp, "/"); localeDir = pstring (tmp, NULL); } } else localeDir = pstring ($3, NULL); } | I_TRUSTEDCERTSDIR { confExpectingToken = D_STRING; } D_STRING { pfree (sslCertsPath, NULL); sslCertsPath = pstring ($3, NULL); } | I_MAXMMAPSIZE D_NUMBER { maxMmapSize = $2; } | I_PIDFILE { confExpectingToken = D_STRING; } D_STRING { pidFile = pstring ($3, NULL); } | I_ACCOUNTERSOCK { confExpectingToken = D_STRING; } D_STRING { accPath = pstring ($3, NULL); } | I_LIMITFILESIZE limit { #ifdef RLIMIT_FSIZE struct rlimit lim; if (getrlimit (RLIMIT_FSIZE, &lim)) syslog (LOG_WARNING, "Failed to get current file size limit: %m."); else { lim.rlim_cur = $2; if (setrlimit (RLIMIT_FSIZE, &lim)) syslog (LOG_WARNING, "Failed to set current file size limit: %m."); } #else syslog (LOG_WARNING, "LimitFileSize used, but OS does not support it."); #endif } | I_HANDLEFAILEDFORK bool { selfFailedFork = ($2 != 0); } ; server: servstart servdata servend ; servstart: '<' I_SERVER { confExpectingToken = D_STRING; } D_STRING '>' { char *np; for (np = $4; *np; np++) { if (isspace (*np & 0xFF)) { syslog (LOG_ERR, "A server name can't contain whitespace (%s).", $4); YYABORT; } } if (find_named_server ($4, 0, NULL, NULL)) { syslog (LOG_ERR, "Two servers can not have the same name (%s).", $4); YYABORT; } currserv = new_server($4, currserv); servtop = talloc (sizeof (*servtop)); if(!currserv || !servtop) YYABORT; servtop->item = currserv; servtop->next = servstack; servstack = servtop; } ; servend: I_ETAG I_SERVER '>' { servstack = servstack->next; currserv = servstack? servstack->item : NULL; } ; servdata: /* empty */ | servdata servst ; servst: server | servopt semicolon | user | directory ; servopt: I_PORT D_NUMBER { add_server_port(currserv, $2); } | I_BIND { confExpectingToken = D_STRING; } D_STRING { add_server_binding(currserv, $3); } | I_RANGE { confExpectingToken = D_STRING; } D_STRING { char *m = strchr ($3, '/'), *ep; struct addrinfo hints = { AI_NUMERICHOST }, *res, *curr; int err, i, j; struct sockaddr_in m4; struct sockaddr_in6 m6; if (m) { *m++ = 0; if (!*m) { *--m = '/'; syslog (LOG_ERR, "%s: Mask missing.", $3); YYABORT; } } err = getaddrinfo ($3, NULL, &hints, &res); if (err || !res) { syslog (LOG_ERR, "Failed to lookup %s: %s", $3, gai_strerror (err)); YYABORT; } m4.sin_family = AF_INET; m6.sin6_family = AF_INET6; for (curr = res; curr; curr = curr->ai_next) { switch (curr->ai_family) { case PF_INET: if (m) { #ifdef HAVE_INET_ATON if (!inet_aton (m, &m4.sin_addr)) #else m4.sin_addr.s_addr = inet_addr (m); if (m4.sin_addr.s_addr == -1) #endif { syslog (LOG_ERR, "%s/%s: Invalid mask.", $3, m); YYABORT; } if (!(*(char*)&m4.sin_addr & 0x80)) { i = ntohl (m4.sin_addr.s_addr); if (i > 32) { syslog (LOG_ERR, "%s/%s: Mask is bigger than " "address length.", $3, m); YYABORT; } memset (&m4.sin_addr, 0xff, i / 8); if (i < 32) memset ((char*)&m4.sin_addr + i / 8 + 1, 0, 4 - i / 8); for (j = 0x80; i & 0x7; i--, j >>= 1) ((char*)&m4.sin_addr)[i / 8 + 1] |= j; } } else memset (&m4.sin_addr, 0xFF, 4); add_server_range (currserv, curr->ai_addr, (struct sockaddr*)&m4); break; case PF_INET6: if (m) { i = strtoul (m, &ep, 0); if (*ep) { syslog (LOG_ERR, "%s/%s: Invalid mask.", $3, m); YYABORT; } if (i > 128) { syslog (LOG_ERR, "%s/%s: Mask is bigger than " "address length.", $3, m); YYABORT; } memset (&m6.sin6_addr, 0xff, i / 8); if (i < 128) memset ((char*)&m6.sin6_addr + i / 8 + 1, 0, 16 - i / 8); for (j = 0x80; i & 0x7; i--, j >>= 1) ((char*)&m6.sin6_addr)[i / 8 + 1] |= j; } else memset (&m6.sin6_addr, 0xFF, 16); add_server_range (currserv, curr->ai_addr, (struct sockaddr*)&m6); break; default: // Just ignore... break; } } freeaddrinfo (res); } | I_MINPASVPORT D_NUMBER { if($2 < 0) $2 = 0; if($2 > 65535) $2 = 65535; currserv->minPasvPort = $2; if(currserv->maxPasvPort < currserv->minPasvPort) currserv->maxPasvPort = currserv->minPasvPort; } | I_MAXPASVPORT D_NUMBER { if($2 < 0) $2 = 0; if($2 > 65535) $2 = 65535; currserv->maxPasvPort = $2; if(currserv->minPasvPort > currserv->maxPasvPort) currserv->minPasvPort = currserv->maxPasvPort; } | I_PASSIFINVALID bool { currserv->passIfInvalid = ($2 != 0); } | I_ALLOWUNBOUND bool { currserv->allowUnbound = ($2 != 0); } | I_ALLOWFOREIGN bool { currserv->allowForeign = ($2 != 0); } | I_ALLOWOUTOFRANGE bool { currserv->allowOutOfRange = ($2 != 0); } | I_ALLOWLOWPORTS bool { currserv->allowLowPorts = ($2 != 0); } | I_CHROOT { confExpectingToken = D_STRING; } D_STRING { if(currserv->chroot) pfree(currserv->chroot, currserv); currserv->chroot = pstring($3, currserv); } | I_PASSWORDNEEDED bool { currserv->passwordNeeded = ($2 != 0); } | I_ALLOWLOGIN bool { currserv->allowLogin = ($2 != 0); } | I_ALLOWSECLOGIN bool { currserv->allowSecLogin = ($2 != 0); } | I_LOGINFAILEDMSG { confExpectingToken = D_STRING; } D_STRING { if(currserv->loginFailedMsg) pfree(currserv->loginFailedMsg, currserv); currserv->loginFailedMsg = pstring($3, currserv); } | I_USERINVALIDMSG { confExpectingToken = D_STRING; } D_STRING { if(currserv->userInvalidMsg) pfree(currserv->userInvalidMsg, currserv); currserv->userInvalidMsg = pstring($3, currserv); } | I_PASSREQUESTMSG { confExpectingToken = D_STRING; } D_STRING { if(currserv->passRequestMsg) pfree(currserv->passRequestMsg, currserv); currserv->passRequestMsg = pstring($3, currserv); } | I_ANONPASSMSG { confExpectingToken = D_STRING; } D_STRING { if(currserv->anonPassMsg) pfree(currserv->anonPassMsg, currserv); currserv->anonPassMsg = pstring($3, currserv); } | I_WELCOMEMSG { confExpectingToken = D_STRING; } D_STRING { pfree (currserv->welcomeMsg, currserv); currserv->welcomeMsg = pstring($3, currserv); } | I_MAXIDLE D_NUMBER { currserv->maxIdle = $2; } | I_ACCEPTTLS { confExpectingToken = D_STRING; } D_STRING ',' { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_TLS if (currserv->tlsCert) { tls_free_cert (currserv->tlsCert); currserv->tlsCert = NULL; } if ($3[0] != '/') { char *tmp = talloc (strlen (sslCertsPath) + strlen ($3) + 2); if (tmp) { strcpy (tmp, sslCertsPath); strcat (tmp, "/"); strcat (tmp, $3); currserv->tlsCert = tls_read_cert (tmp); } } else currserv->tlsCert = tls_read_cert ($3); if (!currserv->tlsCert) { syslog (LOG_ERR, "Failed to read certificate file: %m"); errno = EINVAL; YYABORT; } if (currserv->tlsKey) { tls_free_key (currserv->tlsKey); currserv->tlsKey = NULL; } if ($6[0] != '/') { char *tmp = talloc (strlen (sslCertsPath) + strlen ($6) + 2); if (tmp) { strcpy (tmp, sslCertsPath); strcat (tmp, "/"); strcat (tmp, $6); currserv->tlsKey = tls_read_key (tmp); } } else currserv->tlsKey = tls_read_key ($6); if (!currserv->tlsKey) { syslog (LOG_ERR, "Error reading key: %m"); errno = EINVAL; YYABORT; } #else syslog (LOG_WARNING, "Warning: AcceptTLS used but TLS " "support is not compiled."); #endif } | I_TLSVERIFYCLIENT D_BOOL { #ifdef USE_TLS if ($2) currserv->tlsOptions |= tlsVerifyClient; else currserv->tlsOptions &= ~tlsVerifyClient; #else syslog (LOG_WARNING, "Warning: TlsVerifyClient was used " "but TLS support is not compiled."); #endif } | I_TLSAUTOLOGIN D_BOOL { #ifdef USE_TLS currserv->loginTLS = ($2 != 0); #else syslog (LOG_WARNING, "Warning: TlsAutoLogin was used " "but TLS support is not compiled."); #endif } | I_TLSNONEWUSER D_BOOL { #ifdef USE_TLS currserv->tlsNoLogout = ($2 != 0); #else syslog (LOG_WARNING, "Warning: TlsNoNewUser was used " "but TLS support is not compiled."); #endif } | I_USERALIAS { confExpectingToken = D_STRING; } D_STRING ',' { confExpectingToken = D_STRING; } D_STRING { server_add_alias (currserv, $6, $3); } | I_SLEEPONFAIL D_NUMBER { currserv->sleepOnFail = $2; } | I_MAXLOGINATTEMPTS D_NUMBER { currserv->maxLoginAttempts = $2; } | I_PAMSERVICE { confExpectingToken = D_STRING; } D_STRING { #ifdef HAVE_LIBPAM pfree (currserv->pam_service, currserv); currserv->pam_service = pstring ($3, currserv); #else syslog (LOG_WARNING, "Warning: PamService used but PAM " "support is not compiled."); #endif } | I_DEFAULTHARDLINK bool { currserv->defHardLink = $2? 1 : 0; } | I_DIRECTORYMSGFILE { confExpectingToken = D_STRING; } D_STRING { pfree (currserv->dirMsgFile, currserv); currserv->dirMsgFile = pstring ($3, currserv); } | I_SQLCONNECT { confExpectingToken = D_STRING; } D_STRING ',' { confExpectingToken = D_STRING; } D_STRING ',' { confExpectingToken = D_STRING; } D_STRING ',' { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL currserv->sql.type = sqlINVALID; #ifdef HAVE_MYSQL if (!strcmp ($3, "mysql")) currserv->sql.type = sqlMYSQL; #endif if (currserv->sql.type == sqlINVALID) { syslog (LOG_ERR, "Invalid sql type (%s).", $3); YYABORT; } pfree (currserv->sqlHost, currserv); currserv->sqlHost = pstring ($6, currserv); pfree (currserv->sqlUser, currserv); currserv->sqlUser = pstring ($9, currserv); pfree (currserv->sqlDB, currserv); currserv->sqlDB = pstring ($12, currserv); pfree (currserv->sqlPass, currserv); currserv->sqlPass = NULL; pfree (currserv->sqlCert, currserv); currserv->sqlCert = NULL; pfree (currserv->sqlKey, currserv); currserv->sqlKey = NULL; #else syslog (LOG_WARNING, "Warning: SQLConnect was used but SQL support is not compiled."); #endif } | I_SQLPASSWORD { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL pfree (currserv->sqlPass, currserv); currserv->sqlPass = pstring ($3, currserv); #endif } | I_SQLTLSCONNECT { confExpectingToken = D_STRING; } D_STRING ',' { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL pfree (currserv->sqlCert, currserv); currserv->sqlCert = pstring ($3, currserv); pfree (currserv->sqlKey, currserv); currserv->sqlKey = pstring ($6, currserv); #if defined(HAVE_MYSQL) && !defined(HAVE_MYSQL_SSL_SET) if (currserv->sql.type == sqlMYSQL) syslog (LOG_WARNING, "Warning: SQLTLSConnect used but MySQL library lacks TLS support."); #endif #endif } | I_SQLUSERQUERY { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL pfree (currserv->sqlUserQuery, currserv); currserv->sqlUserQuery = pstring ($3, currserv); #endif } | I_SQLDIRQUERY { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL pfree (currserv->sqlDirQuery, currserv); currserv->sqlDirQuery = pstring ($3, currserv); #endif } | I_SQLCONNECTQUERY { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL pfree (currserv->sqlConnectQuery, currserv); currserv->sqlConnectQuery = pstring ($3, currserv); #endif } | I_MAXCONNECTS D_NUMBER { currserv->maxConnects = $2; } | I_RESET srvrstlist ; srvrstlist: srvrstitem | srvrstlist ',' srvrstitem ; srvrstitem: I_PORT { if(currserv->ports) pfree(currserv->ports, currserv); currserv->ports = NULL; currserv->numPorts = 0; } | I_BIND { if(currserv->bindings) pfree(currserv->bindings, currserv); currserv->bindings = NULL; currserv->numBindings = 0; } | I_LOGINFAILEDMSG { if(currserv->loginFailedMsg) pfree(currserv->loginFailedMsg, currserv); currserv->loginFailedMsg = NULL; } | I_USERINVALIDMSG { if(currserv->userInvalidMsg) pfree(currserv->userInvalidMsg, currserv); currserv->userInvalidMsg = NULL; } | I_PASSREQUESTMSG { if(currserv->passRequestMsg) pfree(currserv->passRequestMsg, currserv); currserv->passRequestMsg = NULL; } | I_ANONPASSMSG { if(currserv->anonPassMsg) pfree(currserv->anonPassMsg, currserv); currserv->anonPassMsg = NULL; } | I_DIRECTORYMSGFILE { pfree (currserv->dirMsgFile, currserv); currserv->dirMsgFile = NULL; } | I_WELCOMEMSG { pfree (currserv->welcomeMsg, currserv); currserv->welcomeMsg = NULL; } | I_RANGE { pfree (currserv->ranges, currserv); currserv->ranges = NULL; currserv->numRanges = 0; } | I_SQLUSERQUERY { #ifdef USE_SQL pfree (currserv->sqlUserQuery, currserv); currserv->sqlUserQuery = NULL; #endif } | I_SQLDIRQUERY { #ifdef USE_SQL pfree (currserv->sqlDirQuery, currserv); currserv->sqlDirQuery = NULL; #endif } | I_SQLCONNECTQUERY { #ifdef USE_SQL pfree (currserv->sqlConnectQuery, currserv); currserv->sqlConnectQuery = NULL; #endif } ; user: userstart userdata userend ; userstart: '<' I_USER { confExpectingToken = D_STRING; } D_STRING '>' { char *np; for (np = $4; *np; np++) { if (isspace (*np & 0xFF)) { syslog (LOG_ERR, "A user name can't contain whitespace(%s). Aliases can.", $4); YYABORT; } } if (curruser) { curruser = copy_user (curruser, $4, currserv); if (!curruser) YYABORT; } else { curruser = find_server_user (currserv, $4, currserv); if (!curruser) { curruser = new_user ($4, currserv); curruser->allowLogin = currserv->allowLogin; curruser->allowSecLogin = currserv->allowSecLogin; curruser->allowForeign = currserv->allowForeign; curruser->allowOutOfRange = currserv->allowOutOfRange; } if (!curruser) YYABORT; pfree (curruser->access, curruser); curruser->access = pattach(currserv->access, curruser); curruser->defHardLink = currserv->defHardLink; curruser->maxIdle = currserv->maxIdle; curruser->passwordNeeded = currserv->passwordNeeded; curruser->dirMsgFile = pattach (currserv->dirMsgFile, curruser); #ifdef USE_SQL curruser->sqlDirQuery = pattach (currserv->sqlDirQuery, curruser); #endif // Reset uid and gid, but don't activate in spec_flags. curruser->uid = unprivUid; curruser->gid = unprivGid; if(currserv->chroot) { pfree (curruser->chroot, curruser); curruser->chroot = pattach(currserv->chroot, curruser); curruser->spec_flags |= ufChroot; } } usertop = talloc (sizeof (*usertop)); if (!usertop) YYABORT; usertop->item = curruser; usertop->next = userstack; userstack = usertop; } ; userend: I_ETAG I_USER '>' { if(curruser->chroot) { char *tmp = curruser->chroot; char *home = curruser->home; user_t *pwUser = NULL; if (!home) { pwUser = find_passwd_user (curruser->name, NULL); if (pwUser && pwUser->home) home = pwUser->home; } curruser->chroot = pstring(full_path(curruser->chroot, NULL, home), curruser); pfree(tmp, curruser); pfree (pwUser, NULL); } curruser->next = padopt (currserv->users, currserv, curruser); currserv->users = curruser; userstack = userstack->next; curruser = userstack? userstack->item : NULL; } ; userdata: /* empty */ | userdata userst ; userst: user | useropt semicolon | directory ; useropt: I_ANONYMOUS bool { curruser->anonymous = ($2 != 0); curruser->spec_flags |= ufAnonymous; if(curruser->anonymous) { curruser->allowLogin = 1; curruser->passwordNeeded = 1; } } | I_ALIAS { confExpectingToken = D_STRING; } D_STRING { server_add_alias(currserv, $3, curruser->name); } | I_HOME { confExpectingToken = D_STRING; } D_STRING { pfree(curruser->home, curruser); curruser->home = pstring(full_path ($3, NULL, NULL), curruser); curruser->spec_flags |= ufHome; } | I_CHROOT { confExpectingToken = D_STRING; } D_STRING { pfree(curruser->chroot, curruser); curruser->chroot = pstring($3, curruser); curruser->spec_flags |= ufChroot; } | I_PASSWORDNEEDED bool { curruser->passwordNeeded = ($2 != 0); } | I_PASSWORD { confExpectingToken = D_STRING; } D_STRING { pfree (curruser->password, curruser); curruser->password = pstring($3, curruser); curruser->passwordNeeded = 1; curruser->anonymous = 0; curruser->spec_flags |= ufPassword | ufAnonymous; } | I_UID { confExpectingToken = D_UID; } D_UID { curruser->spec_flags |= ufUid; curruser->uid = $3; } | I_GID { confExpectingToken = D_GID; } D_GID { curruser->spec_flags |= ufGid; curruser->gid = $3; } | I_ALLOWLOGIN bool { curruser->allowLogin = ($2 != 0); } | I_ALLOWSECLOGIN bool { curruser->allowSecLogin = ($2 != 0); } | I_ALLOWFOREIGN bool { curruser->allowForeign = ($2 != 0); } | I_ALLOWOUTOFRANGE bool { curruser->allowOutOfRange = ($2 != 0); } | I_MAXIDLE D_NUMBER { curruser->maxIdle = $2; } | I_EXTERNLOGIN { confExpectingToken = D_STRING; } D_STRING { pfree (curruser->external_login, curruser); curruser->external_login = pstring (full_path ($3, NULL, curruser->home), curruser); } | I_EXTERNACCESS { confExpectingToken = D_STRING; } D_STRING { pfree (curruser->access_program, curruser); curruser->access_program = pstring (full_path ($3, NULL, curruser->home), curruser); } | I_DEFAULTHARDLINK bool { curruser->defHardLink = $2? 1 : 0; } | I_DIRECTORYMSGFILE { confExpectingToken = D_STRING; } D_STRING { pfree (curruser->dirMsgFile, curruser); curruser->dirMsgFile = pstring ($3, curruser); } | I_SQLDIRQUERY { confExpectingToken = D_STRING; } D_STRING { #ifdef USE_SQL pfree (curruser->sqlDirQuery, curruser); curruser->sqlDirQuery = pstring ($3, curruser); #endif } | I_ADMIN adminlist { curruser->adminPrivs = $2; } | I_MAXLOGINS D_NUMBER { curruser->maxLogins = $2; } | I_RESET usrrstlist ; adminlist: adminitem { $$ = $1; } | adminlist ',' adminitem { $$ = $1 | $3; } ; adminitem: I_LIST { $$ = admList; } | I_MSG { $$ = admMsg; } | I_ABORT { $$ = admAbort; } | I_DISCONNECT { $$ = admDisconnect; } | I_RELOAD { $$ = admReload; } | I_ALL { $$ = -1; } ; usrrstlist: usrrstitem | usrrstlist ',' usrrstitem ; usrrstitem: I_UID { curruser->spec_flags &= ~ufUid; } | I_GID { curruser->spec_flags &= ~ufGid; } | I_HOME { curruser->spec_flags &= ~ufHome; if(curruser->home) pfree(curruser->home, curruser); curruser->home = NULL; } | I_PASSWORD { curruser->spec_flags &= ~ufPassword; } | I_EXTERNLOGIN { pfree (curruser->external_login, curruser); curruser->external_login = NULL; } | I_EXTERNACCESS { pfree (curruser->access_program, curruser); curruser->access_program = NULL; } | I_DIRECTORYMSGFILE { pfree (curruser->dirMsgFile, curruser); curruser->dirMsgFile = NULL; } | I_SQLDIRQUERY { #ifdef USE_SQL pfree (curruser->sqlDirQuery, curruser); curruser->sqlDirQuery = NULL; #endif } | I_ADMIN { curruser->adminPrivs = 0; } ; directory: dirstart dirdata dirend ; dirstart: '<' I_DIRECTORY { confExpectingToken = D_STRING; } D_STRING '>' { access_t *p; p = currdir; currdir = palloc(sizeof(access_t), NULL, NULL); if(!currdir) YYABORT; currdir->path = pstring (full_path ($4, p? p->path : NULL, curruser? curruser->home : NULL), currdir); dirtop = talloc (sizeof (*dirtop)); if (!dirtop) YYABORT; dirtop->item = currdir; dirtop->next = dirstack; dirstack = dirtop; } ; dirend: I_ETAG I_DIRECTORY '>' { access_t *p = NULL, *a = NULL; dirstack = dirstack->next; if(curruser) { if (currdir->numMasks || !curruser->access || !curruser->access->numMasks) { currdir->next = padopt(curruser->access, curruser, currdir); curruser->access = pattach(currdir, curruser); } else { p = curruser->access; if (pnparents (p) > 1) { a = palloc (sizeof (access_t), curruser, NULL); if (!a) YYABORT; pfree (p, curruser); curruser->access = a; } } } else { if (currdir->numMasks || !currserv->access || !currserv->access->numMasks) { currdir->next = padopt(currserv->access, currserv, currdir); currserv->access = pattach(currdir, currserv); } else { p = currserv->access; if (pnparents (p) > 1) { a = palloc (sizeof (access_t), currserv, NULL); if (!a) YYABORT; pfree (p, currserv); currserv->access = a; } } } if (p) { // We need to split up the access chain down to the place // where we should insert. while (1) { if (a) { memcpy (a, p, sizeof (*a)); a->path = pattach (p->path, a); a->masks = pattach (p->masks, a); a->fakeUser = pattach (p->fakeUser, a); a->fakeGroup = pattach (p->fakeGroup, a); a->fakeFile = pattach (p->fakeFile, a); a->fakeDir = pattach (p->fakeDir, a); a->next = pattach (p->next, a); p = a; } if (!p->next || !p->next->numMasks) break; if (a) { a = palloc (sizeof (access_t), p, NULL); if (!a) YYABORT; p = p->next; pfree (p->next, p); p->next = a; } else p = p->next; } currdir->next = padopt (p->next, p, currdir); p->next = pattach (currdir, p); } currdir = dirstack? dirstack->item : NULL; } ; dirdata: /* empty */ | dirdata dirst ; dirst: directory | diropt semicolon ; diropt: I_ALLOW accesslist { currdir->deny &= ~$2; currdir->require &= ~$2; currdir->allow |= $2; } | I_DENY accesslist { currdir->allow &= ~$2; currdir->require &= ~$2; currdir->deny |= $2; } | I_REQUIRE accesslist { $2 &= acEncrypted | acSigned; currdir->allow &= ~$2; currdir->deny &= ~$2; currdir->require |= $2; } | I_HIDDEN bool { currdir->hidden = ($2 != 0); } | I_FAKEUSER { confExpectingToken = D_STRING; } D_STRING { pfree(currdir->fakeUser, currdir); currdir->fakeUser = pstring($3, currdir); } | I_FAKEGROUP { confExpectingToken = D_STRING; } D_STRING { pfree(currdir->fakeGroup, currdir); currdir->fakeGroup = pstring($3, currdir); } | I_FAKEFILEMODE { confExpectingToken = D_STRING; } D_STRING { pfree(currdir->fakeFile, currdir); currdir->fakeFile = pstring($3, currdir); } | I_FAKEDIRMODE { confExpectingToken = D_STRING; } D_STRING { pfree(currdir->fakeDir, currdir); currdir->fakeDir = pstring($3, currdir); } | I_HARDLINK bool { currdir->hardlink = $2? 1 : -1; } | I_DIRECTORYMSGFILE { confExpectingToken = D_STRING; } D_STRING { pfree (currdir->dirMsgFile, currdir); currdir->dirMsgFile = pstring ($3, currdir); } | I_MASK { confExpectingToken = D_STRING; } D_STRING { if (currdir->numMasks) currdir->masks = prealloc (currdir->masks, sizeof (*currdir->masks) * (currdir->numMasks + 1)); else currdir->masks = palloc (sizeof (*currdir->masks), currdir, NULL); if (!currdir->masks) YYABORT; currdir->masks[currdir->numMasks++] = pstring ($3, currdir->masks); } | I_RESET dirrstlist ; dirrstlist: dirrstitem | dirrstlist ',' dirrstitem ; dirrstitem: I_FAKEUSER { if(currdir->fakeUser != (char*)-1) pfree(currdir->fakeUser, currdir); currdir->fakeUser = (char*)-1; } | I_FAKEGROUP { if(currdir->fakeGroup != (char*)-1) pfree(currdir->fakeGroup, currdir); currdir->fakeGroup = (char*)-1; } | I_FAKEFILEMODE { if(currdir->fakeFile != (char*)-1) pfree(currdir->fakeFile, currdir); currdir->fakeFile = (char*)-1; } | I_FAKEDIRMODE { if(currdir->fakeDir != (char*)-1) pfree(currdir->fakeDir, currdir); currdir->fakeDir = (char*)-1; } | I_DIRECTORYMSGFILE { pfree (currdir->dirMsgFile, currdir); currdir->dirMsgFile = NULL; } ; accesslist: accessitem { $$ = $1; } | accesslist ',' accessitem { $$ = $1 | $3; } ; accessitem: I_SEARCH { $$ = acSearch; } | I_READFILE { $$ = acReadFile; } | I_LISTING { $$ = acListing; } | I_CREATEFILE { $$ = acCreateFile; } | I_CREATEDIR { $$ = acCreateDir; } | I_APPEND { $$ = acAppend; } | I_OVERWRITE { $$ = acOverwrite; } | I_DELETE { $$ = acDelete; } | I_RENAME { $$ = acRename; } | I_ENCRYPTED { $$ = acEncrypted; } | I_SIGNED { $$ = acSigned; } | I_READING { $$ = acSearch | acReadFile | acListing; } | I_WRITING { $$ = acCreateFile | acCreateDir | acAppend | acOverwrite | acDelete; } | I_STORING { $$ = acCreateFile | acCreateDir | acAppend; } | I_ALL { $$ = -1; } ; %%