autofs-5.0.9 - amd lookup add selector handling From: Ian Kent --- include/parse_subs.h | 2 lib/parse_subs.c | 127 ++++++++++++++++++ modules/amd_parse.y | 49 ++++++- modules/amd_tok.l | 73 ++++++++++ modules/parse_amd.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 586 insertions(+), 13 deletions(-) diff --git a/include/parse_subs.h b/include/parse_subs.h index a416c59..43da182 100644 --- a/include/parse_subs.h +++ b/include/parse_subs.h @@ -111,6 +111,8 @@ struct map_type_info { }; unsigned int get_proximity(struct sockaddr *); +unsigned int get_network_proximity(const char *); +unsigned int in_network(char *); const char *skipspace(const char *); int check_colon(const char *); int chunklen(const char *, int); diff --git a/lib/parse_subs.c b/lib/parse_subs.c index f485a4c..de5319f 100644 --- a/lib/parse_subs.c +++ b/lib/parse_subs.c @@ -19,9 +19,13 @@ #include #include #include +#include #include #include #include +#include +#include + #include "automount.h" #define MAX_OPTIONS_LEN 256 @@ -370,6 +374,129 @@ unsigned int get_proximity(struct sockaddr *host_addr) return PROXIMITY_OTHER; } +static char *inet_fill_net(const char *net_num, char *net) +{ + char *np; + unsigned int dots = 3; + + if (strlen(net_num) > INET_ADDRSTRLEN) + return NULL; + + if (!isdigit(*net_num)) + return NULL; + + *net = '\0'; + strcpy(net, net_num); + + np = net; + while (*np++) { + if (*np == '.') { + np++; + dots--; + if (!*np && dots) + strcat(net, "0"); + continue; + } + + if ((*np && !isdigit(*np)) || dots < 0) { + *net = '\0'; + return NULL; + } + } + + while (dots--) + strcat(net, ".0"); + + return net; +} + +static char *get_network_number(const char *network) +{ + struct netent *netent; + char cnet[MAX_NETWORK_LEN]; + uint32_t h_net; + size_t len; + + len = strlen(network) + 1; + if (len > MAX_NETWORK_LEN) + return NULL; + + netent = getnetbyname(network); + if (!netent) + return NULL; + h_net = ntohl(netent->n_net); + + if (!inet_ntop(AF_INET, &h_net, cnet, INET_ADDRSTRLEN)) + return NULL; + + return strdup(cnet); +} + +unsigned int get_network_proximity(const char *name) +{ + struct addrinfo hints; + struct addrinfo *ni, *this; + char name_or_num[NI_MAXHOST]; + unsigned int proximity; + char *net; + int ret; + + if (!name) + return PROXIMITY_ERROR; + + net = get_network_number(name); + if (net) + strcpy(name_or_num, net); + else { + char this[NI_MAXHOST]; + char *mask; + + strcpy(this, name); + if ((mask = strchr(this, '/'))) + *mask++ = '\0'; + if (!strchr(this, '.')) + strcpy(name_or_num, this); + else { + char buf[NI_MAXHOST], *new; + new = inet_fill_net(this, buf); + if (!new) + return PROXIMITY_ERROR; + strcpy(name_or_num, new); + } + } + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + ret = getaddrinfo(name_or_num, NULL, &hints, &ni); + if (ret) { + logerr("getaddrinfo: %s", gai_strerror(ret)); + return PROXIMITY_ERROR; + } + + proximity = PROXIMITY_OTHER; + + this = ni; + while (this) { + unsigned int prx = get_proximity(this->ai_addr); + if (prx < proximity) + proximity = prx; + this = this->ai_next; + } + + return proximity; +} + +unsigned int in_network(char *network) +{ + unsigned int proximity = get_network_proximity(network); + if (proximity == PROXIMITY_ERROR || + proximity > PROXIMITY_SUBNET) + return 0; + return 1; +} + /* * Skip whitespace in a string; if we hit a #, consider the rest of the * entry a comment. diff --git a/modules/amd_parse.y b/modules/amd_parse.y index 33106a1..71fd569 100644 --- a/modules/amd_parse.y +++ b/modules/amd_parse.y @@ -92,6 +92,9 @@ static int amd_fprintf(FILE *, char *, ...); %token NOT_EQUAL %token COMMA %token OPTION_ASSIGN +%token LBRACKET +%token RBRACKET +%token NOT %token NILL %token MAP_OPTION @@ -102,6 +105,7 @@ static int amd_fprintf(FILE *, char *, ...); %token MNT_OPTION %token SELECTOR %token SELECTOR_VALUE +%token SEL_ARG_VALUE %token OPTION %token MACRO %token OTHER @@ -187,18 +191,46 @@ selector_or_option: selection selection: SELECTOR IS_EQUAL SELECTOR_VALUE { - if (!make_selector($1, $3, NULL, SEL_TYPE_EQUAL)) { + if (!make_selector($1, $3, NULL, SEL_COMP_EQUAL)) { amd_notify($1); YYABORT; } } | SELECTOR NOT_EQUAL SELECTOR_VALUE { - if (!make_selector($1, $3, NULL, SEL_TYPE_NOTEQUAL)) { + if (!make_selector($1, $3, NULL, SEL_COMP_NOTEQUAL)) { amd_notify($1); YYABORT; } } + | SELECTOR LBRACKET SEL_ARG_VALUE RBRACKET + { + if (!make_selector($1, $3, NULL, SEL_COMP_NONE)) { + amd_notify($1); + YYABORT; + } + } + | SELECTOR LBRACKET SEL_ARG_VALUE COMMA SEL_ARG_VALUE RBRACKET + { + if (!make_selector($1, $3, $5, SEL_COMP_NONE)) { + amd_notify($1); + YYABORT; + } + } + | NOT SELECTOR LBRACKET SEL_ARG_VALUE RBRACKET + { + if (!make_selector($2, $4, NULL, SEL_COMP_NOT)) { + amd_notify($2); + YYABORT; + } + } + | NOT SELECTOR LBRACKET SEL_ARG_VALUE COMMA SEL_ARG_VALUE RBRACKET + { + if (!make_selector($2, $4, $6, SEL_COMP_NOT)) { + amd_notify($2); + YYABORT; + } + } ; option_assignment: MAP_OPTION OPTION_ASSIGN FS_TYPE @@ -388,9 +420,6 @@ static int make_selector(char *name, if (!sel_lookup(name)) return 0; - if (!value1) - return 0; - s = get_selector(name); if (!s) return 0; @@ -401,9 +430,13 @@ static int make_selector(char *name, goto error; s->comp.value = tmp; } else if (s->sel->flags & SEL_FLAG_FUNC1) { - char *tmp = amd_strdup(value1); - if (!tmp) - goto error; + if (!value1) + tmp = NULL; + else { + char *tmp = amd_strdup(value1); + if (!tmp) + goto error; + } s->func.arg1 = tmp; } else if (s->sel->flags & SEL_FLAG_FUNC2) { char *tmp = amd_strdup(value1); diff --git a/modules/amd_tok.l b/modules/amd_tok.l index afa3a87..cea9ea5 100644 --- a/modules/amd_tok.l +++ b/modules/amd_tok.l @@ -70,22 +70,29 @@ int amd_yyinput(char *, int); %option nounput -%x MAPOPTVAL FSOPTVAL MNTOPTVAL SELOPTVAL +%x MAPOPTVAL FSOPTVAL MNTOPTVAL SELOPTVAL SELARGVAL NL \r?\n OPTWS [[:blank:]]* OTHR [^!;:=/|\- \t\r\n#]* +V4NUM ([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5]) + MACRO (\$\{([[:alpha:]_/]([[:alnum:]_\-])([[:alnum:]_\-/])*)\}) QSTR (\"([^"\\]|\\.)*\") OSTR ([[:alpha:]]([[:alnum:]_\-])+) FSTR ([[:alnum:]_/\.]([[:alnum:]_\-/\.]|(\\.))*) VSTR (([[:alnum:]_\-\:/\.])+) SSTR ([[:alpha:]]([[:alnum:]\-\.])+) +IP4ADDR ({V4NUM}\.((({V4NUM}\.){0,2}){V4NUM}?)) +V4MASK ({IP4ADDR}|([1-2][0-9]|3[0-2]|[1-9])) +IP6ADDR ((([A-Fa-f0-9]{1,4}\:\:?){1,7}[A-Fa-f0-9]{1,4})|(\:\:1)) +V6MASK (12[0-8]|1[0-1][0-9]|[1-9][0-9]|[1-9]) FOPT (({QSTR}|{FSTR}|{MACRO})+) OPTS ({OSTR}(=({VSTR}|{QSTR}|{MACRO})+)?) SOPT (({SSTR}|{QSTR}|{MACRO})+) +NOPT ({SSTR}|(({IP4ADDR}(\/{V4MASK})?)|({IP6ADDR}(\/{V6MASK})?))) MAPOPT (fs|type|maptype|pref|sublink|delay) MNTOPT (opts|addopts|remopts) @@ -94,13 +101,16 @@ MAPTYPE (file|nis|nisplus|ldap|hesiod|exec|ndbm|passwd|union) FSTYPE (auto|nfs|link|host|nfsx|ufs|xfs|efs) OSSEL (arch|karch|os|osver|full_os|vendor) -HSTSEL (host|hostd|xhost|domain|byte|cluster) -NETSEL (netnumber|network|wire|netgrp|netgrpd|in_network) +HSTSEL (host|hostd|domain|byte|cluster) +NETSEL (netnumber|network|wire|in_network) USRSEL (uid|gid) MAPSEL (key|map|path) -OTRSEL (exists|autodir|dollar) +OTRSEL (autodir|dollar) BOLSEL (true|false) -SELOPT ({OSSEL}|{HSTSEL}|{NETSEL}|{BOLSEL}|{USRSEL}|{MAPSEL}|{OTRSEL}) + +SELOPT ({OSSEL}|{HSTSEL}|{USRSEL}|{MAPSEL}|{OTRSEL}) +SEL1ARG (xhost|exists|{NETSEL}|{BOLSEL}) +SEL2ARG (netgrp|netgrpd) CUTSEP (\|\||\/) @@ -135,6 +145,20 @@ CUTSEP (\|\||\/) return SELECTOR; } + "!"/({SEL1ARG}|{SEL2ARG}) { return NOT; } + + {SEL1ARG} { + BEGIN(SELARGVAL); + strcpy(amd_lval.strtype, amd_text); + return SELECTOR; + } + + {SEL2ARG} { + BEGIN(SELARGVAL); + strcpy(amd_lval.strtype, amd_text); + return SELECTOR; + } + {CUTSEP} { return CUT; } "-" { return HYPHEN; } @@ -263,6 +287,45 @@ CUTSEP (\|\||\/) } } +{ + {NL} | + \x00 { + BEGIN(INITIAL); + yyless(1); + } + + ";" { + BEGIN(INITIAL); + return SEPERATOR; + } + + "(" { return LBRACKET; } + + {NOPT} { + strcpy(amd_lval.strtype, amd_text); + return SEL_ARG_VALUE; + } + + {SOPT}/"," { + strcpy(amd_lval.strtype, amd_text); + return SEL_ARG_VALUE; + } + + "," { return COMMA; } + + {SOPT} { + strcpy(amd_lval.strtype, amd_text); + return SEL_ARG_VALUE; + } + + {FOPT} { + strcpy(amd_lval.strtype, amd_text); + return SEL_ARG_VALUE; + } + + ")" { return RBRACKET; } +} + %% #include "automount.h" diff --git a/modules/parse_amd.c b/modules/parse_amd.c index 35cc5dc..d9c7d9b 100644 --- a/modules/parse_amd.c +++ b/modules/parse_amd.c @@ -223,6 +223,307 @@ static struct substvar *add_lookup_vars(struct autofs_point *ap, return list; } +static int match_my_name(unsigned int logopt, const char *name, struct substvar *sv) +{ + struct addrinfo hints, *cni, *ni, *haddr; + char host[NI_MAXHOST + 1], numeric[NI_MAXHOST + 1]; + const struct substvar *v; + int rv = 0, ret; + + v = macro_findvar(sv, "host", 4); + if (v) { + if (!strcmp(v->val, name)) + return 1; + } + + /* Check if comparison value is an alias */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + /* Get host canonical name */ + ret = getaddrinfo(v->val, NULL, &hints, &cni); + if (ret) { + error(logopt, + "hostname lookup failed: %s\n", gai_strerror(ret)); + goto out; + } + + hints.ai_flags = 0; + + /* Resolve comparison name to its names and compare */ + ret = getaddrinfo(name, NULL, &hints, &ni); + if (ret) { + error(logopt, + "hostname lookup failed: %s\n", gai_strerror(ret)); + freeaddrinfo(cni); + goto out; + } + + haddr = ni; + while (haddr) { + /* Translate the host address into a numeric string form */ + ret = getnameinfo(haddr->ai_addr, haddr->ai_addrlen, + numeric, sizeof(numeric), NULL, 0, + NI_NUMERICHOST); + if (ret) { + error(logopt, + "host address info lookup failed: %s\n", + gai_strerror(ret)); + freeaddrinfo(cni); + goto next; + } + + /* Try to resolve back again to get the canonical name */ + ret = getnameinfo(haddr->ai_addr, haddr->ai_addrlen, + host, NI_MAXHOST, NULL, 0, 0); + if (ret) { + error(logopt, + "host address info lookup failed: %s\n", + gai_strerror(ret)); + freeaddrinfo(cni); + goto next; + } + + if (!strcmp(host, cni->ai_canonname)) { + rv = 1; + break; + } +next: + haddr = haddr->ai_next; + } + freeaddrinfo(ni); + freeaddrinfo(cni); +out: + return rv; +} + +static int eval_selector(unsigned int logopt, + struct amd_entry *this, struct substvar *sv) +{ + struct selector *s = this->selector; + const struct substvar *v; + unsigned int s_type; + unsigned int v_type; + struct stat st; + char *host; + int res, val, ret = 0; + + s_type = s->sel->flags & SEL_FLAGS_TYPE_MASK; + + switch (s_type) { + case SEL_FLAG_MACRO: + v = macro_findvar(sv, s->sel->name, strlen(s->sel->name)); + if (!v) { + error(logopt, "failed to get selector %s", s->sel->name); + return 0; + } + + v_type = s->sel->flags & SEL_FLAGS_VALUE_MASK; + + switch (v_type) { + case SEL_FLAG_STR: + res = strcmp(v->val, s->comp.value); + if (s->compare & SEL_COMP_EQUAL && !res) { + debug(logopt, MODPREFIX + "matched selector %s(%s) == %s", + v->def, v->val, s->comp.value); + ret = 1; + break; + } else if (s->compare & SEL_COMP_NOTEQUAL && res) { + debug(logopt, MODPREFIX + "matched selector %s(%s) != %s", + v->def, v->val, s->comp.value); + ret = 1; + break; + } + + debug(logopt, MODPREFIX + "did not match selector %s(%s) %s %s", + v->def, v->val, + (s->compare & SEL_COMP_EQUAL ? "==" : "!="), + s->comp.value); + break; + + case SEL_FLAG_NUM: + res = atoi(v->val); + val = atoi(s->comp.value); + if (s->compare & SEL_COMP_EQUAL && res == val) { + debug(logopt, MODPREFIX + "matched selector %s(%s) equal to %s", + v->def, v->val, s->comp.value); + ret = 1; + break; + } else if (s->compare & SEL_COMP_NOTEQUAL && res != val) { + debug(logopt, MODPREFIX + "matched selector %s(%s) not equal to %s", + v->def, v->val, s->comp.value); + ret = 1; + break; + } + + debug(logopt, MODPREFIX + "did not match selector %s(%s) %s %s", + v->def, v->val, + (s->compare & SEL_COMP_EQUAL ? "==" : "!="), + s->comp.value); + break; + + default: + break; + } + break; + + case SEL_FLAG_FUNC1: + if (s->sel->selector != SEL_TRUE && + s->sel->selector != SEL_FALSE && + !s->func.arg1) { + error(logopt, MODPREFIX + "expected argument missing for selector %s", + s->sel->name); + break; + } + + switch (s->sel->selector) { + case SEL_TRUE: + ret = 1; + if (s->compare == SEL_COMP_NOT) + ret = !ret; + if (ret) + debug(logopt, MODPREFIX + "matched selector %s(%s)", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "did not match selector %s(%s)", + s->sel->name, s->func.arg1); + break; + + case SEL_FALSE: + if (s->compare == SEL_COMP_NOT) + ret = !ret; + if (ret) + debug(logopt, MODPREFIX + "matched selector %s(%s)", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "did not match selector %s(%s)", + s->sel->name, s->func.arg1); + break; + + case SEL_XHOST: + ret = match_my_name(logopt, s->func.arg1, sv); + if (s->compare == SEL_COMP_NOT) + ret = !ret; + if (ret) + debug(logopt, MODPREFIX + "matched selector %s(%s) to host name", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "did not match selector %s(%s) to host name", + s->sel->name, s->func.arg1); + break; + + case SEL_EXISTS: + /* Sould be OK to fail on any error here */ + ret = !lstat(s->func.arg1, &st); + if (s->compare == SEL_COMP_NOT) + ret = !ret; + if (ret) + debug(logopt, MODPREFIX + "matched selector %s(%s)", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "did not match selector %s(%s)", + s->sel->name, s->func.arg1); + break; + + case SEL_IN_NETWORK: + ret = in_network(s->func.arg1); + if (s->compare == SEL_COMP_NOT) + ret = !ret; + if (ret) + debug(logopt, MODPREFIX + "matched selector %s(%s)", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "did not match selector %s(%s)", + s->sel->name, s->func.arg1); + break; + + default: + break; + } + break; + + case SEL_FLAG_FUNC2: + if (!s->func.arg1) { + error(logopt, MODPREFIX + "expected argument missing for selector %s", + s->sel->name); + break; + } + + switch (s->sel->selector) { + case SEL_NETGRP: + case SEL_NETGRPD: + if (s->func.arg2) + host = s->func.arg2; + else { + if (s->sel->selector == SEL_NETGRP) + v = macro_findvar(sv, "host", 4); + else + v = macro_findvar(sv, "hostd", 5); + if (!v || !*v->val) { + error(logopt, + "failed to get value of ${host}"); + break; + } + host = v->val; + } + ret = innetgr(s->func.arg1, host, NULL, NULL); + if (s->compare == SEL_COMP_NOT) + ret = !ret; + if (ret) { + if (!s->func.arg2) + debug(logopt, MODPREFIX + "matched selector %s(%s)", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "matched selector %s(%s,%s)", + s->sel->name, s->func.arg1, + s->func.arg2); + } else { + if (!s->func.arg2) + debug(logopt, MODPREFIX + "did not match selector %s(%s)", + s->sel->name, s->func.arg1); + else + debug(logopt, MODPREFIX + "did not match selector %s(%s,%s)", + s->sel->name, s->func.arg1, s->func.arg2); + } + break; + + default: + break; + } + break; + + default: + break; + } + + return ret; +} + static void update_with_defaults(struct amd_entry *defaults, struct amd_entry *entry, struct substvar *sv) @@ -884,6 +1185,33 @@ static void update_prefix(struct autofs_point *ap, return; } +static int match_selectors(unsigned int logopt, + struct amd_entry *entry, struct substvar *sv) +{ + struct selector *s = entry->selector; + int ret; + + /* No selectors, always match */ + if (!s) { + debug(logopt, "no selectors found in location"); + return 1; + } + + ret = 0; + + /* All selectors must match */ + while (s) { + ret = eval_selector(logopt, entry, sv); + if (!ret) + break; + s = s->next; + } + if (!s) + ret = 1; + + return ret; +} + static struct amd_entry *dup_defaults_entry(struct amd_entry *defaults) { struct amd_entry *entry; @@ -1008,6 +1336,23 @@ static struct amd_entry *select_default_entry(struct autofs_point *ap, free_amd_entry(this); continue; } + + /* + * This probably should be a fail since we expect + * selectors to pick the default entry. + */ + if (!this->selector) + continue; + + if (match_selectors(ap->logopt, this, sv)) { + if (entry_default) { + /*update_with_defaults(entry_default, this, sv);*/ + free_amd_entry(entry_default); + } + list_del_init(&this->list); + defaults_entry = this; + break; + } } /* Not strickly amd semantics but ... */ @@ -1195,6 +1540,9 @@ int parse_mount(struct autofs_point *ap, const char *name, continue; } + if (!match_selectors(ap->logopt, this, sv)) + continue; + update_with_defaults(cur_defaults, this, sv); sv = expand_entry(ap, this, flags, sv); sv = merge_entry_options(ap, this, sv);