2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. 2N/A * PSARC/2004/154 nfsmapid DNS enhancements implementation. 2N/A * As per RFC 3530, file owner and group attributes in version 4 of the 2N/A * NFS protocol are no longer exchanged between client and server as 32 2N/A * bit integral values. Instead, owner and group file attributes are 2N/A * exchanged between client and server as UTF8 strings of form 2N/A * 'user@domain' (ie. "joeblow@central.sun.com") 2N/A * 'group@domain' (ie. "staff@central.sun.com") 2N/A * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as 2N/A * being able to describe a user with a unique string identifier provides 2N/A * a much more powerful and administrative friendly way of dealing with 2N/A * overlaps in the uid/gid number spaces. That notwithstanding, dealing 2N/A * with issues of correctly mapping user and group ownership in a cross- 2N/A * domain environment has proven a difficult problem to solve, since 2N/A * dealing with different permutations of client naming configurations 2N/A * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users 2N/A * utilizing clients and servers that have the 'domain' portion of the 2N/A * UTF8 attribute string configured differently than its peer server and 2N/A * client accordingly, will experience watching their files owned by the 2N/A * 'nobody' user and group. This is due to the fact that the 'domain's 2N/A * don't match and the nfsmapid daemon treats the attribute strings as 2N/A * unknown user(s) or group(s) (even though the actual uid/gid's may exist 2N/A * in the executing daemon's system). Please refer to PSARC/2004/154 for 2N/A * further background and motivation for these enhancements. 2N/A * The latest implementation of the nfsmapid daemon relies on a DNS TXT 2N/A * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN 2N/A * set, then the nfsmapid daemon queries the configured DNS domain server 2N/A * for the _nfsv4idmapdomain TXT record. If the record exists, then the 2N/A * record's value is used as the 'domain' portion of the UTF8 attribute 2N/A * strings. If the TXT record has not been configured in the DNS server, 2N/A * then the daemon falls back to using the DNS domain name itself as the 2N/A * 'domain' portion of the attribute strings. Lastly, if the configured 2N/A * DNS server is unresponsive, the nfsmapid daemon falls back to using 2N/A * the DNS domain name as the 'domain' portion of the attribute strings, 2N/A * and fires up a query thread to keep contacting the DNS server until 2N/A * it responds with either a TXT record, or a lack thereof, in which 2N/A * case, nfsmapid just continues to utilize the DNS domain name. 2N/A * Decode any resolver errors and print out message to log 2N/A * Nameserver is not responding. 2N/A * Try again after a given timeout. 2N/A * This msg only really happens once, due 2N/A * to s_dns_disabled flag (see below) 2N/A * No entries in the nameserver for 2N/A * the specific record or record type. 2N/A * Reset the global state variables used for the TXT record. 2N/A * Having these values reset to zero helps nfsmapid confirm 2N/A * that a valid DNS TXT record was not found; in which case, 2N/A * it would fall back to using the configured DNS domain name. 2N/A * If a valid DNS TXT record _was_ found, but subsequent contact 2N/A * to the DNS server is somehow hindered, the previous DNS TXT 2N/A * RR value continues to be used. Thus, in such instances, we 2N/A * forego clearing the global config variables so nfsmapid can 2N/A * continue to use a valid DNS TXT RR while contact to the DNS 2N/A * server is reestablished. 2N/A * Initialize resolver and populate &s_res struct 2N/A * DNS Domain is saved off sysdns_domain in case we 2N/A * need to fall back to using the DNS domain name as 2N/A * the v4 attribute string domain. 2N/A * Search criteria assumptions: 2N/A * The onus will fall on the sysadmins to correctly configure the TXT 2N/A * record in the DNS domain where the box currently resides in order 2N/A * for the record to be found. However, if they sysadmin chooses to 2N/A * _will_ traverse up the DNS tree as specified in the 'search' key. 2N/A * Otherwise, we'll default the domain to the DNS domain itself. 2N/A * Avoid holding locks across the res_nsearch() call to 2N/A * prevent stalling threads during network partitions. 2N/A * Free all resolver state information stored in s_res 2N/A * Skip one DNS record 2N/A * Skip compressed name 2N/A * Advance pointer and make sure 2N/A * we're still within the message 2N/A * Now, just skip over the rr fields 2N/A * Process one TXT record. 2N/A * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain 2N/A * TXT record. Thus, if the TXT record exists, the answer section of 2N/A * the DNS response carries the TXT record's value. Thus, we check that 2N/A * the value is indeed a valid domain and set the modular s_txt_rr 2N/A * global to the domain value. 2N/A return;
/* process next TXT RR */ 2N/A * make sure we have a clean buf since 2N/A * we may've processed several TXT rr's 2N/A * If there is a record and it's a valid domain, we're done. 2N/A * Otherwise, log the error 2N/A * Decode any answer received from the DNS server. This interface is 2N/A * capable of much more than just decoding TXT records. We maintain 2N/A * focus on TXT rr's for now, but this will probably change once we 2N/A * get the IETF approved application specific DNS RR. 2N/A * Here's an example of the TXT record we're decoding (as would appear 2N/A * in the DNS zone file): 2N/A * Once the IETF application specific DNS RR is granted, we should only 2N/A * be changing the record flavor, but all should pretty much stay the 2N/A * Check the HEADER for any signs of errors 2N/A * and extract the answer counts for later. 2N/A * skip query entries 2N/A * If debugging... print query only once. 2N/A * NOTE: Don't advance pointer... this is done 2N/A * in while() loop on a per record basis ! 2N/A * Process actual answer(s). 2N/A /* skip the name field */ 2N/A * Advance to next answer record for any 2N/A * other record types. Again, this will 2N/A * probably change (see block comment). 2N/A * Skip name server and additional records for now. 2N/A * If a valid TXT record entry exists, s_txt_rr contains the domain 2N/A * value (as set in resolv_process_txt) and we extract the value into 2N/A * dns_txt_domain (the exported global). If there was _no_ valid TXT 2N/A * entry, we simply return and check_domain() will default to the 2N/A * DNS domain since we did resolv_txt_reset() first. 2N/A * Make sure values passed are sane and initialize accordingly. 2N/A * Update the library's mapid_domain variable if 'dname' is different. 2N/A * If the caller gave us a valid callback routine, we 2N/A * instantiate it to announce the domain change, but 2N/A * only if either the domain changed _or_ the caller 2N/A * was issued a SIGHUP. 2N/A * Thread to keep pinging DNS server for TXT record if nfsmapid's 2N/A * initial attempt at contact with server failed. We could potentially 2N/A * have a substantial number of NFSv4 clients and having all of them 2N/A * hammering on an already unresponsive DNS server would not help 2N/A * things. So, we limit the number of live query threads to at most 2N/A * 1 at any one time to keep things from getting out of hand. 2N/A * Failed to initialize resolver. Do not 2N/A * This is a bit different than what we 2N/A * do in get_dns_txt_domain(), where we 2N/A * simply return and let the caller 2N/A * access dns_txt_domain directly. 2N/A * Here we invoke the callback routine 2N/A * provided by the caller to the 2N/A * mapid_reeval_domain() interface via 2N/A * the cb_t's fcn param. 2N/A * DNS is up now, but does not have 2N/A * the NFSV4IDMAPDOMAIN TXT record. 2N/A * Non-Recoverable error occurred. No sense 2N/A * in keep pinging the DNS server at this 2N/A * point, so we disable any further contact. 2N/A * Authoritative NS not responding... 2N/A * keep trying for non-authoritative reply 2N/A /* mark thread as done */ 2N/A * nfsmapid's interface into the resolver for getting the TXT record. 2N/A * o If the DNS server is available and the TXT record is found, we 2N/A * simply decode the output and fill the exported dns_txt_domain 2N/A * global, so our caller can configure the daemon appropriately. 2N/A * o If the TXT record is not found, then having done resolv_txt_reset() 2N/A * first will allow our caller to recognize that the exported globals 2N/A * are empty and thus configure nfsmapid to use the default DNS domain. 2N/A * there is no name server address information. We return since we've 2N/A * already have reset the TXT global state. 2N/A * o If a previous call to the DNS server resulted in an unrecoverable 2N/A * error, then we disable further contact to the DNS server and return. 2N/A * Having the TXT global state already reset guarantees that our caller 2N/A * will fall back to the right configuration. 2N/A * o Query thread creation is throttled by s_dns_qthr_created. We mitigate 2N/A * the problem of an already unresponsive DNS server by allowing at most 2N/A * 1 outstanding query thread since we could potentially have a substantial 2N/A * amount of clients hammering on the same DNS server attempting to get 2N/A * is missing or we've had unrecoverable resolver errors, 2N/A * we'll default to get_dns_domain(). If a previous DNS 2N/A * TXT RR was found, don't clear it until we're certain 2N/A * that contact can be made to the DNS server (see block 2N/A * comment atop resolv_txt_reset). If we're responding to 2N/A * a SIGHUP signal, force a reset of the cached copy. 2N/A * get the domain from domainname(1M). No real 2N/A * reason to query DNS or fire a thread since we 2N/A * have no nameserver addresses. 2N/A * If there were non-recoverable problems with DNS, 2N/A * we have stopped querying DNS entirely. See 2N/A * NO_RECOVERY clause below. 2N/A * Failed to initialize resolver. Do not 2N/A * If there _is_ a TXT record, we let 2N/A * our caller set the global state. 2N/A * If no valid argument was passed or 2N/A * callback defined, don't fire thread 2N/A * We may have lots of clients, so we don't 2N/A * want to bog down the DNS server with tons 2N/A * of requests... lest it becomes even more 2N/A * unresponsive, so limit 1 thread to query 2N/A * DNS did not respond ! Set timeout and kick off 2N/A * thread to try op again after s_dns_tout seconds. 2N/A * We've made sure that we don't have an already 2N/A * running thread above. 2N/A * For any other errors... DNS is responding, but 2N/A * either it has no data, or some other problem is 2N/A * occuring. At any rate, the TXT domain should not 2N/A * be used, so we default to the DNS domain. 2N/A * trim_wspace is a destructive interface; it is up to 2N/A * the caller to save off an original copy if needed. 2N/A * Any empty domain is not valid 2N/A * Skip leading blanks 2N/A * If we reached the end of the string w/o 2N/A * finding a non-blank char, return error 2N/A * Find next blank in string 2N/A for (r =
ndp; *r !=
'\0'; r++) {
2N/A * No more blanks found, we are done 2N/A * Terminate string at blank 2N/A * Skip any trailing spaces 2N/A while (*r !=
'\0') {
2N/A * If a non-blank is found, it is an 2N/A * illegal domain (embedded blanks). 2N/A * Get NFSMAPID_DOMAIN property value from SMF. 2N/A * NFSMAPID_DOMAIN was set, so it's time for validation. If 2N/A * it's okay, then update NFS domain and return. If not, 2N/A * bail (syslog in DEBUG). We make nfsmapid more a bit 2N/A * more forgiving of trailing and leading white space. 2N/A * So the NFS SMF parameter nfsmapid_domain cannot be obtained or 2N/A * there is an invalid nfsmapid_domain property value. 2N/A * Time to zap current NFS domain info. 2N/A * If we can't get stats for the config file, then 2N/A * zap the DNS domain info. If mtime hasn't changed, 2N/A * then there's no work to do, so just return. 2N/A * The resolver defaults to obtaining the 2N/A * domain off of the NIS domainname(1M) if 2N/A * Re-initialize resolver to zap DNS domain from previous 2N/A * resolv_init() calls. 2N/A * Update cached DNS domain. No need for validation since 2N/A * domain comes from resolver. If resolver doesn't return the 2N/A * domain, then zap the DNS domain. This shouldn't ever happen, 2N/A * and if it does, the machine has bigger problems (so no need 2N/A * to generate a message that says DNS appears to be broken). 2N/A * PSARC 2005/487 Contracted Sun Private Interface 2N/A * mapid_stdchk_domain() 2N/A * Changes must be reviewed by Solaris File Sharing 2N/A * Changes must be communicated to contract-2005-487-01@sun.com 2N/A * Based on the recommendations from RFC1033 and RFC1035, check 2N/A * if a given domain name string is valid. Return values are: 2N/A * 1 = valid domain name 2N/A * 0 = invalid domain name (or invalid embedded character) 2N/A * -1 = domain length > NS_MAXCDNAME 2N/A * 1st _AND_ last char _must_ be alphanumeric. 2N/A * We check for other valid chars below. 2N/A (*
ds !=
'.') && (*
ds !=
'-') && (*
ds !=
'_'))
2N/A * PSARC 2005/487 Consolidation Private 2N/A * mapid_reeval_domain() 2N/A * Changes must be reviewed by Solaris File Sharing 2N/A * We're either here because: 2N/A * . No suitable DNS TXT resource record exists 2N/A * . DNS server is not responding to requests 2N/A * in either case, we want to default to using the 2N/A * system configured DNS domain. If this fails, then 2N/A * dns_domain will be empty and dns_domain_len will 2N/A * PSARC 2005/487 Consolidation Private 2N/A * mapid_get_domain() 2N/A * Changes must be reviewed by Solaris File Sharing 2N/A * The use of TSD in mapid_get_domain() diverges slightly from the typical 2N/A * TSD use, since here, the benefit of doing TSD is mostly to allocate 2N/A * a per-thread buffer that will be utilized by other up-calls to the 2N/A * In doors, the thread used for the upcall never really exits, hence 2N/A * the typical destructor function defined via thr_keycreate() will 2N/A * never be called. Thus, we only use TSD to allocate the per-thread 2N/A * buffer and fill it up w/the configured 'mapid_domain' on each call. 2N/A * This still alleviates the problem of having the caller free any 2N/A * PSARC 2005/487 Contracted Sun Private Interface 2N/A * mapid_derive_domain() 2N/A * Changes must be reviewed by Solaris File Sharing 2N/A * Changes must be communicated to contract-2005-487-01@sun.com 2N/A * This interface is called solely via sysidnfs4 iff no 2N/A * NFSMAPID_DOMAIN was found. So, there is no ill effect 2N/A * of having the reeval function call get_nfs_domain().