1*8200fe25Srmesta /* 2*8200fe25Srmesta * CDDL HEADER START 3*8200fe25Srmesta * 4*8200fe25Srmesta * The contents of this file are subject to the terms of the 5*8200fe25Srmesta * Common Development and Distribution License (the "License"). 6*8200fe25Srmesta * You may not use this file except in compliance with the License. 7*8200fe25Srmesta * 8*8200fe25Srmesta * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*8200fe25Srmesta * or http://www.opensolaris.org/os/licensing. 10*8200fe25Srmesta * See the License for the specific language governing permissions 11*8200fe25Srmesta * and limitations under the License. 12*8200fe25Srmesta * 13*8200fe25Srmesta * When distributing Covered Code, include this CDDL HEADER in each 14*8200fe25Srmesta * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*8200fe25Srmesta * If applicable, add the following below this CDDL HEADER, with the 16*8200fe25Srmesta * fields enclosed by brackets "[]" replaced with your own identifying 17*8200fe25Srmesta * information: Portions Copyright [yyyy] [name of copyright owner] 18*8200fe25Srmesta * 19*8200fe25Srmesta * CDDL HEADER END 20*8200fe25Srmesta */ 21*8200fe25Srmesta /* 22*8200fe25Srmesta * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*8200fe25Srmesta * Use is subject to license terms. 24*8200fe25Srmesta */ 25*8200fe25Srmesta 26*8200fe25Srmesta #pragma ident "%Z%%M% %I% %E% SMI" 27*8200fe25Srmesta 28*8200fe25Srmesta /* 29*8200fe25Srmesta * PSARC/2004/154 nfsmapid DNS enhancements implementation. 30*8200fe25Srmesta * 31*8200fe25Srmesta * As per RFC 3530, file owner and group attributes in version 4 of the 32*8200fe25Srmesta * NFS protocol are no longer exchanged between client and server as 32 33*8200fe25Srmesta * bit integral values. Instead, owner and group file attributes are 34*8200fe25Srmesta * exchanged between client and server as UTF8 strings of form 35*8200fe25Srmesta * 36*8200fe25Srmesta * 'user@domain' (ie. "joeblow@central.sun.com") 37*8200fe25Srmesta * 'group@domain' (ie. "staff@central.sun.com") 38*8200fe25Srmesta * 39*8200fe25Srmesta * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as 40*8200fe25Srmesta * being able to describe a user with a unique string identifier provides 41*8200fe25Srmesta * a much more powerful and administrative friendly way of dealing with 42*8200fe25Srmesta * overlaps in the uid/gid number spaces. That notwithstanding, dealing 43*8200fe25Srmesta * with issues of correctly mapping user and group ownership in a cross- 44*8200fe25Srmesta * domain environment has proven a difficult problem to solve, since 45*8200fe25Srmesta * dealing with different permutations of client naming configurations 46*8200fe25Srmesta * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users 47*8200fe25Srmesta * utilizing clients and servers that have the 'domain' portion of the 48*8200fe25Srmesta * UTF8 attribute string configured differently than its peer server and 49*8200fe25Srmesta * client accordingly, will experience watching their files owned by the 50*8200fe25Srmesta * 'nobody' user and group. This is due to the fact that the 'domain's 51*8200fe25Srmesta * don't match and the nfsmapid daemon treats the attribute strings as 52*8200fe25Srmesta * unknown user(s) or group(s) (even though the actual uid/gid's may exist 53*8200fe25Srmesta * in the executing daemon's system). Please refer to PSARC/2004/154 for 54*8200fe25Srmesta * further background and motivation for these enhancements. 55*8200fe25Srmesta * 56*8200fe25Srmesta * The latest implementation of the nfsmapid daemon relies on a DNS TXT 57*8200fe25Srmesta * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN 58*8200fe25Srmesta * configuration option in /etc/default/nfs. If the option has not been 59*8200fe25Srmesta * set, then the nfsmapid daemon queries the configured DNS domain server 60*8200fe25Srmesta * for the _nfsv4idmapdomain TXT record. If the record exists, then the 61*8200fe25Srmesta * record's value is used as the 'domain' portion of the UTF8 attribute 62*8200fe25Srmesta * strings. If the TXT record has not been configured in the DNS server, 63*8200fe25Srmesta * then the daemon falls back to using the DNS domain name itself as the 64*8200fe25Srmesta * 'domain' portion of the attribute strings. Lastly, if the configured 65*8200fe25Srmesta * DNS server is unresponsive, the nfsmapid daemon falls back to using 66*8200fe25Srmesta * the DNS domain name as the 'domain' portion of the attribute strings, 67*8200fe25Srmesta * and fires up a query thread to keep contacting the DNS server until 68*8200fe25Srmesta * it responds with either a TXT record, or a lack thereof, in which 69*8200fe25Srmesta * case, nfsmapid just continues to utilize the DNS domain name. 70*8200fe25Srmesta */ 71*8200fe25Srmesta #define __LIBMAPID_IMPL 72*8200fe25Srmesta #include <nfs/mapid.h> 73*8200fe25Srmesta #pragma init(_lib_init) 74*8200fe25Srmesta 75*8200fe25Srmesta /* 76*8200fe25Srmesta * DEBUG Only 77*8200fe25Srmesta * Decode any resolver errors and print out message to log 78*8200fe25Srmesta */ 79*8200fe25Srmesta static int 80*8200fe25Srmesta resolv_error(void) 81*8200fe25Srmesta { 82*8200fe25Srmesta #ifndef DEBUG 83*8200fe25Srmesta 84*8200fe25Srmesta return (h_errno); 85*8200fe25Srmesta 86*8200fe25Srmesta #else /* DEBUG */ 87*8200fe25Srmesta 88*8200fe25Srmesta static uint64_t msg_done[NS_ERRS] = {0}; 89*8200fe25Srmesta 90*8200fe25Srmesta switch (h_errno) { 91*8200fe25Srmesta case NETDB_INTERNAL: 92*8200fe25Srmesta syslog(LOG_ERR, EMSG_NETDB_INTERNAL, strerror(errno)); 93*8200fe25Srmesta break; 94*8200fe25Srmesta 95*8200fe25Srmesta case HOST_NOT_FOUND: 96*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 97*8200fe25Srmesta msg_done[h_errno]++; 98*8200fe25Srmesta if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE)) 99*8200fe25Srmesta syslog(LOG_ERR, EMSG_HOST_NOT_FOUND, s_dname); 100*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 101*8200fe25Srmesta break; 102*8200fe25Srmesta 103*8200fe25Srmesta case TRY_AGAIN: 104*8200fe25Srmesta /* 105*8200fe25Srmesta * Nameserver is not responding. 106*8200fe25Srmesta * Try again after a given timeout. 107*8200fe25Srmesta */ 108*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 109*8200fe25Srmesta msg_done[h_errno]++; 110*8200fe25Srmesta if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE)) 111*8200fe25Srmesta syslog(LOG_ERR, EMSG_TRY_AGAIN, s_dname); 112*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 113*8200fe25Srmesta break; 114*8200fe25Srmesta 115*8200fe25Srmesta case NO_RECOVERY: 116*8200fe25Srmesta /* 117*8200fe25Srmesta * This msg only really happens once, due 118*8200fe25Srmesta * to s_dns_disabled flag (see below) 119*8200fe25Srmesta */ 120*8200fe25Srmesta syslog(LOG_ERR, EMSG_NO_RECOVERY, hstrerror(h_errno)); 121*8200fe25Srmesta break; 122*8200fe25Srmesta 123*8200fe25Srmesta case NO_DATA: 124*8200fe25Srmesta /* 125*8200fe25Srmesta * No entries in the nameserver for 126*8200fe25Srmesta * the specific record or record type. 127*8200fe25Srmesta */ 128*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 129*8200fe25Srmesta msg_done[h_errno]++; 130*8200fe25Srmesta if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE)) 131*8200fe25Srmesta syslog(LOG_ERR, EMSG_NO_DATA, NFSMAPID_DNS_RR, s_dname); 132*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 133*8200fe25Srmesta break; 134*8200fe25Srmesta 135*8200fe25Srmesta case NETDB_SUCCESS: 136*8200fe25Srmesta default: 137*8200fe25Srmesta break; 138*8200fe25Srmesta } 139*8200fe25Srmesta return (h_errno); 140*8200fe25Srmesta 141*8200fe25Srmesta #endif /* DEBUG */ 142*8200fe25Srmesta } 143*8200fe25Srmesta 144*8200fe25Srmesta /* 145*8200fe25Srmesta * Reset the global state variables used for the TXT record. 146*8200fe25Srmesta * Having these values reset to zero helps nfsmapid confirm 147*8200fe25Srmesta * that a valid DNS TXT record was not found; in which case, 148*8200fe25Srmesta * it would fall back to using the configured DNS domain name. 149*8200fe25Srmesta * 150*8200fe25Srmesta * If a valid DNS TXT record _was_ found, but subsequent contact 151*8200fe25Srmesta * to the DNS server is somehow hindered, the previous DNS TXT 152*8200fe25Srmesta * RR value continues to be used. Thus, in such instances, we 153*8200fe25Srmesta * forego clearing the global config variables so nfsmapid can 154*8200fe25Srmesta * continue to use a valid DNS TXT RR while contact to the DNS 155*8200fe25Srmesta * server is reestablished. 156*8200fe25Srmesta */ 157*8200fe25Srmesta static void 158*8200fe25Srmesta resolv_txt_reset(void) 159*8200fe25Srmesta { 160*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 161*8200fe25Srmesta bzero(s_txt_rr, sizeof (s_txt_rr)); 162*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 163*8200fe25Srmesta 164*8200fe25Srmesta (void) rw_wrlock(&s_dns_data_lock); 165*8200fe25Srmesta if (!dns_txt_cached) { 166*8200fe25Srmesta dns_txt_domain_len = 0; 167*8200fe25Srmesta bzero(dns_txt_domain, DNAMEMAX); 168*8200fe25Srmesta } 169*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 170*8200fe25Srmesta } 171*8200fe25Srmesta 172*8200fe25Srmesta /* 173*8200fe25Srmesta * Initialize resolver and populate &s_res struct 174*8200fe25Srmesta * 175*8200fe25Srmesta * DNS Domain is saved off sysdns_domain in case we 176*8200fe25Srmesta * need to fall back to using the DNS domain name as 177*8200fe25Srmesta * the v4 attribute string domain. 178*8200fe25Srmesta */ 179*8200fe25Srmesta static int 180*8200fe25Srmesta resolv_init(void) 181*8200fe25Srmesta { 182*8200fe25Srmesta size_t len; 183*8200fe25Srmesta int n; 184*8200fe25Srmesta struct __res_state res; 185*8200fe25Srmesta 186*8200fe25Srmesta (void) mutex_lock(&s_res_lock); 187*8200fe25Srmesta bzero(&s_res, sizeof (struct __res_state)); 188*8200fe25Srmesta n = h_errno = errno = 0; 189*8200fe25Srmesta if ((n = res_ninit(&s_res)) < 0) { 190*8200fe25Srmesta (void) mutex_unlock(&s_res_lock); 191*8200fe25Srmesta (void) resolv_error(); 192*8200fe25Srmesta return (n); 193*8200fe25Srmesta } 194*8200fe25Srmesta res = s_res; 195*8200fe25Srmesta (void) mutex_unlock(&s_res_lock); 196*8200fe25Srmesta 197*8200fe25Srmesta len = strlen(res.defdname) + 1; 198*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 199*8200fe25Srmesta bzero(s_dname, sizeof (s_dname)); 200*8200fe25Srmesta (void) snprintf(s_dname, len, "%s", res.defdname); 201*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 202*8200fe25Srmesta 203*8200fe25Srmesta (void) rw_wrlock(&s_dns_data_lock); 204*8200fe25Srmesta (void) snprintf(sysdns_domain, len, "%s", res.defdname); 205*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 206*8200fe25Srmesta 207*8200fe25Srmesta return (0); 208*8200fe25Srmesta } 209*8200fe25Srmesta 210*8200fe25Srmesta /* 211*8200fe25Srmesta * Search criteria assumptions: 212*8200fe25Srmesta * 213*8200fe25Srmesta * The onus will fall on the sysadmins to correctly configure the TXT 214*8200fe25Srmesta * record in the DNS domain where the box currently resides in order 215*8200fe25Srmesta * for the record to be found. However, if they sysadmin chooses to 216*8200fe25Srmesta * add the 'search' key to /etc/resolv.conf, then resolv_search() 217*8200fe25Srmesta * _will_ traverse up the DNS tree as specified in the 'search' key. 218*8200fe25Srmesta * Otherwise, we'll default the domain to the DNS domain itself. 219*8200fe25Srmesta */ 220*8200fe25Srmesta static int 221*8200fe25Srmesta resolv_search(void) 222*8200fe25Srmesta { 223*8200fe25Srmesta int len; 224*8200fe25Srmesta ans_t ans = {0}; 225*8200fe25Srmesta struct __res_state res; 226*8200fe25Srmesta int type = T_TXT; 227*8200fe25Srmesta int class = C_IN; 228*8200fe25Srmesta 229*8200fe25Srmesta (void) mutex_lock(&s_res_lock); 230*8200fe25Srmesta res = s_res; 231*8200fe25Srmesta (void) mutex_unlock(&s_res_lock); 232*8200fe25Srmesta 233*8200fe25Srmesta /* 234*8200fe25Srmesta * Avoid holding locks across the res_nsearch() call to 235*8200fe25Srmesta * prevent stalling threads during network partitions. 236*8200fe25Srmesta */ 237*8200fe25Srmesta len = h_errno = errno = 0; 238*8200fe25Srmesta if ((len = res_nsearch(&res, NFSMAPID_DNS_RR, class, type, 239*8200fe25Srmesta ans.buf, sizeof (ans))) < 0) 240*8200fe25Srmesta return (resolv_error()); 241*8200fe25Srmesta 242*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 243*8200fe25Srmesta s_ans = ans; 244*8200fe25Srmesta s_anslen = len; 245*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 246*8200fe25Srmesta 247*8200fe25Srmesta return (NETDB_SUCCESS); 248*8200fe25Srmesta } 249*8200fe25Srmesta 250*8200fe25Srmesta /* 251*8200fe25Srmesta * Skip one DNS record 252*8200fe25Srmesta */ 253*8200fe25Srmesta static uchar_t * 254*8200fe25Srmesta resolv_skip_rr(uchar_t *p, uchar_t *eom) 255*8200fe25Srmesta { 256*8200fe25Srmesta int t; 257*8200fe25Srmesta int dlen; 258*8200fe25Srmesta 259*8200fe25Srmesta /* 260*8200fe25Srmesta * Skip compressed name 261*8200fe25Srmesta */ 262*8200fe25Srmesta errno = 0; 263*8200fe25Srmesta if ((t = dn_skipname(p, eom)) < 0) { 264*8200fe25Srmesta #ifdef DEBUG 265*8200fe25Srmesta syslog(LOG_ERR, "%s", strerror(errno)); 266*8200fe25Srmesta #endif 267*8200fe25Srmesta return (NULL); 268*8200fe25Srmesta } 269*8200fe25Srmesta 270*8200fe25Srmesta /* 271*8200fe25Srmesta * Advance pointer and make sure 272*8200fe25Srmesta * we're still within the message 273*8200fe25Srmesta */ 274*8200fe25Srmesta p += t; 275*8200fe25Srmesta if ((p + RRFIXEDSZ) > eom) 276*8200fe25Srmesta return (NULL); 277*8200fe25Srmesta 278*8200fe25Srmesta /* 279*8200fe25Srmesta * Now, just skip over the rr fields 280*8200fe25Srmesta */ 281*8200fe25Srmesta p += INT16SZ; /* type */ 282*8200fe25Srmesta p += INT16SZ; /* class */ 283*8200fe25Srmesta p += INT32SZ; /* ttl */ 284*8200fe25Srmesta dlen = ns_get16(p); 285*8200fe25Srmesta p += INT16SZ; 286*8200fe25Srmesta p += dlen; /* dlen */ 287*8200fe25Srmesta if (p > eom) 288*8200fe25Srmesta return (NULL); 289*8200fe25Srmesta 290*8200fe25Srmesta return (p); 291*8200fe25Srmesta } 292*8200fe25Srmesta 293*8200fe25Srmesta /* 294*8200fe25Srmesta * Process one TXT record. 295*8200fe25Srmesta * 296*8200fe25Srmesta * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain 297*8200fe25Srmesta * TXT record. Thus, if the TXT record exists, the answer section of 298*8200fe25Srmesta * the DNS response carries the TXT record's value. Thus, we check that 299*8200fe25Srmesta * the value is indeed a valid domain and set the modular s_txt_rr 300*8200fe25Srmesta * global to the domain value. 301*8200fe25Srmesta */ 302*8200fe25Srmesta static void 303*8200fe25Srmesta resolve_process_txt(uchar_t *p, int dlen) 304*8200fe25Srmesta { 305*8200fe25Srmesta char *rr_base = (char *)(p + 1); 306*8200fe25Srmesta char *rr_end = (char *)(p + dlen); 307*8200fe25Srmesta size_t len = rr_end - rr_base; 308*8200fe25Srmesta #ifdef DEBUG 309*8200fe25Srmesta static uint64_t msg_done = 0; 310*8200fe25Srmesta #endif 311*8200fe25Srmesta char tmp_txt_rr[DNAMEMAX]; 312*8200fe25Srmesta 313*8200fe25Srmesta if (len >= DNAMEMAX) 314*8200fe25Srmesta return; /* process next TXT RR */ 315*8200fe25Srmesta 316*8200fe25Srmesta /* 317*8200fe25Srmesta * make sure we have a clean buf since 318*8200fe25Srmesta * we may've processed several TXT rr's 319*8200fe25Srmesta */ 320*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 321*8200fe25Srmesta bzero(s_txt_rr, sizeof (s_txt_rr)); 322*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 323*8200fe25Srmesta 324*8200fe25Srmesta (void) strncpy(tmp_txt_rr, rr_base, len); 325*8200fe25Srmesta tmp_txt_rr[len] = '\0'; 326*8200fe25Srmesta 327*8200fe25Srmesta /* 328*8200fe25Srmesta * If there is a record and it's a valid domain, we're done. 329*8200fe25Srmesta */ 330*8200fe25Srmesta if (rr_base[0] != '\0' && mapid_stdchk_domain(tmp_txt_rr) > 0) { 331*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 332*8200fe25Srmesta (void) strncpy(s_txt_rr, rr_base, len); 333*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 334*8200fe25Srmesta #ifdef DEBUG 335*8200fe25Srmesta syslog(LOG_ERR, "TXT (Rec):\t%s", s_txt_rr); 336*8200fe25Srmesta 337*8200fe25Srmesta } else if (!(msg_done++ % NFSMAPID_SLOG_RATE)) { 338*8200fe25Srmesta /* 339*8200fe25Srmesta * Otherwise, log the error 340*8200fe25Srmesta */ 341*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 342*8200fe25Srmesta syslog(LOG_ERR, EMSG_DNS_RR_INVAL, NFSMAPID_DNS_RR, s_dname); 343*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 344*8200fe25Srmesta #endif 345*8200fe25Srmesta } 346*8200fe25Srmesta } 347*8200fe25Srmesta 348*8200fe25Srmesta /* 349*8200fe25Srmesta * Decode any answer received from the DNS server. This interface is 350*8200fe25Srmesta * capable of much more than just decoding TXT records. We maintain 351*8200fe25Srmesta * focus on TXT rr's for now, but this will probably change once we 352*8200fe25Srmesta * get the IETF approved application specific DNS RR. 353*8200fe25Srmesta * 354*8200fe25Srmesta * Here's an example of the TXT record we're decoding (as would appear 355*8200fe25Srmesta * in the DNS zone file): 356*8200fe25Srmesta * 357*8200fe25Srmesta * _nfsv4idmapdomain IN TXT "sun.com" 358*8200fe25Srmesta * 359*8200fe25Srmesta * Once the IETF application specific DNS RR is granted, we should only 360*8200fe25Srmesta * be changing the record flavor, but all should pretty much stay the 361*8200fe25Srmesta * same. 362*8200fe25Srmesta */ 363*8200fe25Srmesta static void 364*8200fe25Srmesta resolv_decode(void) 365*8200fe25Srmesta { 366*8200fe25Srmesta uchar_t *buf; 367*8200fe25Srmesta HEADER *hp; 368*8200fe25Srmesta uchar_t name[DNAMEMAX]; 369*8200fe25Srmesta uchar_t *eom; 370*8200fe25Srmesta uchar_t *p; 371*8200fe25Srmesta int n; 372*8200fe25Srmesta uint_t qd_cnt; 373*8200fe25Srmesta uint_t an_cnt; 374*8200fe25Srmesta uint_t ns_cnt; 375*8200fe25Srmesta uint_t ar_cnt; 376*8200fe25Srmesta uint_t cnt; 377*8200fe25Srmesta uint_t type; 378*8200fe25Srmesta int dlen; 379*8200fe25Srmesta ans_t answer = {0}; 380*8200fe25Srmesta int answer_len = 0; 381*8200fe25Srmesta 382*8200fe25Srmesta /* 383*8200fe25Srmesta * Check the HEADER for any signs of errors 384*8200fe25Srmesta * and extract the answer counts for later. 385*8200fe25Srmesta */ 386*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 387*8200fe25Srmesta answer = s_ans; 388*8200fe25Srmesta answer_len = s_anslen; 389*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 390*8200fe25Srmesta 391*8200fe25Srmesta buf = (uchar_t *)&answer.buf; 392*8200fe25Srmesta hp = (HEADER *)&answer.hdr; 393*8200fe25Srmesta eom = (uchar_t *)(buf + answer_len); 394*8200fe25Srmesta if (hp->rcode != NOERROR) { 395*8200fe25Srmesta #ifdef DEBUG 396*8200fe25Srmesta syslog(LOG_ERR, "errno: %s", strerror(errno)); 397*8200fe25Srmesta syslog(LOG_ERR, "h_errno: %s", hstrerror(h_errno)); 398*8200fe25Srmesta #endif 399*8200fe25Srmesta return; 400*8200fe25Srmesta } 401*8200fe25Srmesta qd_cnt = ntohs(hp->qdcount); 402*8200fe25Srmesta an_cnt = ntohs(hp->ancount); 403*8200fe25Srmesta ns_cnt = ntohs(hp->nscount); 404*8200fe25Srmesta ar_cnt = ntohs(hp->arcount); 405*8200fe25Srmesta 406*8200fe25Srmesta /* 407*8200fe25Srmesta * skip query entries 408*8200fe25Srmesta */ 409*8200fe25Srmesta p = (uchar_t *)(buf + HFIXEDSZ); 410*8200fe25Srmesta errno = 0; 411*8200fe25Srmesta while (qd_cnt-- > 0) { 412*8200fe25Srmesta n = dn_skipname(p, eom); 413*8200fe25Srmesta if (n < 0) { 414*8200fe25Srmesta #ifdef DEBUG 415*8200fe25Srmesta syslog(LOG_ERR, "%s", strerror(errno)); 416*8200fe25Srmesta #endif 417*8200fe25Srmesta return; 418*8200fe25Srmesta } 419*8200fe25Srmesta p += n; 420*8200fe25Srmesta p += INT16SZ; /* type */ 421*8200fe25Srmesta p += INT16SZ; /* class */ 422*8200fe25Srmesta } 423*8200fe25Srmesta 424*8200fe25Srmesta #ifdef DEBUG 425*8200fe25Srmesta /* 426*8200fe25Srmesta * If debugging... print query only once. 427*8200fe25Srmesta * NOTE: Don't advance pointer... this is done 428*8200fe25Srmesta * in while() loop on a per record basis ! 429*8200fe25Srmesta */ 430*8200fe25Srmesta n = h_errno = errno = 0; 431*8200fe25Srmesta n = dn_expand(buf, eom, p, (char *)name, sizeof (name)); 432*8200fe25Srmesta if (n < 0) { 433*8200fe25Srmesta (void) resolv_error(); 434*8200fe25Srmesta return; 435*8200fe25Srmesta } 436*8200fe25Srmesta syslog(LOG_ERR, "Query:\t\t%-30s", name); 437*8200fe25Srmesta #endif 438*8200fe25Srmesta 439*8200fe25Srmesta /* 440*8200fe25Srmesta * Process actual answer(s). 441*8200fe25Srmesta */ 442*8200fe25Srmesta cnt = an_cnt; 443*8200fe25Srmesta while (cnt-- > 0 && p < eom) { 444*8200fe25Srmesta /* skip the name field */ 445*8200fe25Srmesta n = dn_expand(buf, eom, p, (char *)name, sizeof (name)); 446*8200fe25Srmesta if (n < 0) { 447*8200fe25Srmesta (void) resolv_error(); 448*8200fe25Srmesta return; 449*8200fe25Srmesta } 450*8200fe25Srmesta p += n; 451*8200fe25Srmesta 452*8200fe25Srmesta if ((p + 3 * INT16SZ + INT32SZ) > eom) 453*8200fe25Srmesta return; 454*8200fe25Srmesta 455*8200fe25Srmesta type = ns_get16(p); 456*8200fe25Srmesta p += INT16SZ; 457*8200fe25Srmesta p += INT16SZ + INT32SZ; /* skip class & ttl */ 458*8200fe25Srmesta dlen = ns_get16(p); 459*8200fe25Srmesta p += INT16SZ; 460*8200fe25Srmesta 461*8200fe25Srmesta if ((p + dlen) > eom) 462*8200fe25Srmesta return; 463*8200fe25Srmesta 464*8200fe25Srmesta switch (type) { 465*8200fe25Srmesta case T_TXT: 466*8200fe25Srmesta resolve_process_txt(p, dlen); 467*8200fe25Srmesta break; 468*8200fe25Srmesta 469*8200fe25Srmesta default: 470*8200fe25Srmesta /* 471*8200fe25Srmesta * Advance to next answer record for any 472*8200fe25Srmesta * other record types. Again, this will 473*8200fe25Srmesta * probably change (see block comment). 474*8200fe25Srmesta */ 475*8200fe25Srmesta p += dlen; 476*8200fe25Srmesta break; 477*8200fe25Srmesta } 478*8200fe25Srmesta } 479*8200fe25Srmesta 480*8200fe25Srmesta /* 481*8200fe25Srmesta * Skip name server and additional records for now. 482*8200fe25Srmesta */ 483*8200fe25Srmesta cnt = ns_cnt + ar_cnt; 484*8200fe25Srmesta if (cnt > 0) { 485*8200fe25Srmesta while (--cnt != 0 && p < eom) { 486*8200fe25Srmesta p = resolv_skip_rr(p, eom); 487*8200fe25Srmesta if (p == NULL) 488*8200fe25Srmesta return; 489*8200fe25Srmesta } 490*8200fe25Srmesta } 491*8200fe25Srmesta } 492*8200fe25Srmesta 493*8200fe25Srmesta /* 494*8200fe25Srmesta * If a valid TXT record entry exists, s_txt_rr contains the domain 495*8200fe25Srmesta * value (as set in resolv_process_txt) and we extract the value into 496*8200fe25Srmesta * dns_txt_domain (the exported global). If there was _no_ valid TXT 497*8200fe25Srmesta * entry, we simply return and check_domain() will default to the 498*8200fe25Srmesta * DNS domain since we did resolv_txt_reset() first. 499*8200fe25Srmesta */ 500*8200fe25Srmesta static void 501*8200fe25Srmesta resolv_get_txt_data() 502*8200fe25Srmesta { 503*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 504*8200fe25Srmesta if (s_txt_rr[0] != '\0') { 505*8200fe25Srmesta (void) rw_wrlock(&s_dns_data_lock); 506*8200fe25Srmesta (void) snprintf(dns_txt_domain, strlen(s_txt_rr) + 1, "%s", 507*8200fe25Srmesta s_txt_rr); 508*8200fe25Srmesta dns_txt_domain_len = strlen(dns_txt_domain); 509*8200fe25Srmesta dns_txt_cached = 1; 510*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 511*8200fe25Srmesta } 512*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 513*8200fe25Srmesta } 514*8200fe25Srmesta 515*8200fe25Srmesta static void 516*8200fe25Srmesta domain_sync(cb_t *argp, char *dname) 517*8200fe25Srmesta { 518*8200fe25Srmesta int dlen = 0; 519*8200fe25Srmesta void *(*fcn)(void *) = NULL; 520*8200fe25Srmesta int sighup = 0; 521*8200fe25Srmesta int domchg = 0; 522*8200fe25Srmesta 523*8200fe25Srmesta /* 524*8200fe25Srmesta * Make sure values passed are sane and initialize accordingly. 525*8200fe25Srmesta */ 526*8200fe25Srmesta if (dname != NULL) 527*8200fe25Srmesta dlen = strlen(dname); 528*8200fe25Srmesta if (argp) { 529*8200fe25Srmesta if (argp->fcn) 530*8200fe25Srmesta fcn = argp->fcn; 531*8200fe25Srmesta if (argp->signal) 532*8200fe25Srmesta sighup = argp->signal; 533*8200fe25Srmesta } 534*8200fe25Srmesta 535*8200fe25Srmesta /* 536*8200fe25Srmesta * Update the library's mapid_domain variable if 'dname' is different. 537*8200fe25Srmesta */ 538*8200fe25Srmesta if (dlen != 0 && strncasecmp(dname, mapid_domain, NS_MAXCDNAME)) { 539*8200fe25Srmesta (void) rw_wrlock(&mapid_domain_lock); 540*8200fe25Srmesta (void) strncpy(mapid_domain, dname, NS_MAXCDNAME); 541*8200fe25Srmesta mapid_domain_len = dlen; 542*8200fe25Srmesta (void) rw_unlock(&mapid_domain_lock); 543*8200fe25Srmesta domchg++; 544*8200fe25Srmesta } 545*8200fe25Srmesta 546*8200fe25Srmesta /* 547*8200fe25Srmesta * If the caller gave us a valid callback routine, we 548*8200fe25Srmesta * instantiate it to announce the domain change, but 549*8200fe25Srmesta * only if either the domain changed _or_ the caller 550*8200fe25Srmesta * was issued a SIGHUP. 551*8200fe25Srmesta */ 552*8200fe25Srmesta if (fcn != NULL && (sighup || domchg)) 553*8200fe25Srmesta (void) fcn((void *)mapid_domain); 554*8200fe25Srmesta } 555*8200fe25Srmesta 556*8200fe25Srmesta /* 557*8200fe25Srmesta * Thread to keep pinging DNS server for TXT record if nfsmapid's 558*8200fe25Srmesta * initial attempt at contact with server failed. We could potentially 559*8200fe25Srmesta * have a substantial number of NFSv4 clients and having all of them 560*8200fe25Srmesta * hammering on an already unresponsive DNS server would not help 561*8200fe25Srmesta * things. So, we limit the number of live query threads to at most 562*8200fe25Srmesta * 1 at any one time to keep things from getting out of hand. 563*8200fe25Srmesta */ 564*8200fe25Srmesta /* ARGSUSED */ 565*8200fe25Srmesta static void * 566*8200fe25Srmesta resolv_query_thread(void *arg) 567*8200fe25Srmesta { 568*8200fe25Srmesta unsigned int nap_time; 569*8200fe25Srmesta 570*8200fe25Srmesta #ifdef DEBUG 571*8200fe25Srmesta char *whoami = "query_thread"; 572*8200fe25Srmesta 573*8200fe25Srmesta syslog(LOG_ERR, "%s active !", whoami); 574*8200fe25Srmesta #endif 575*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 576*8200fe25Srmesta nap_time = s_dns_tout; 577*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 578*8200fe25Srmesta 579*8200fe25Srmesta for (;;) { 580*8200fe25Srmesta (void) sleep(nap_time); 581*8200fe25Srmesta 582*8200fe25Srmesta resolv_txt_reset(); 583*8200fe25Srmesta (void) resolv_init(); 584*8200fe25Srmesta switch (resolv_search()) { 585*8200fe25Srmesta case NETDB_SUCCESS: 586*8200fe25Srmesta resolv_decode(); 587*8200fe25Srmesta resolv_get_txt_data(); 588*8200fe25Srmesta 589*8200fe25Srmesta /* 590*8200fe25Srmesta * This is a bit different than what we 591*8200fe25Srmesta * do in get_dns_txt_domain(), where we 592*8200fe25Srmesta * simply return and let the caller 593*8200fe25Srmesta * access dns_txt_domain directly. 594*8200fe25Srmesta * 595*8200fe25Srmesta * Here we invoke the callback routine 596*8200fe25Srmesta * provided by the caller to the 597*8200fe25Srmesta * mapid_reeval_domain() interface via 598*8200fe25Srmesta * the cb_t's fcn param. 599*8200fe25Srmesta */ 600*8200fe25Srmesta domain_sync((cb_t *)arg, dns_txt_domain); 601*8200fe25Srmesta goto thr_okay; 602*8200fe25Srmesta 603*8200fe25Srmesta case NO_DATA: 604*8200fe25Srmesta /* 605*8200fe25Srmesta * DNS is up now, but does not have 606*8200fe25Srmesta * the NFSV4IDMAPDOMAIN TXT record. 607*8200fe25Srmesta */ 608*8200fe25Srmesta #ifdef DEBUG 609*8200fe25Srmesta syslog(LOG_ERR, "%s: DNS has no TXT Record", whoami); 610*8200fe25Srmesta #endif 611*8200fe25Srmesta goto thr_reset; 612*8200fe25Srmesta 613*8200fe25Srmesta case NO_RECOVERY: 614*8200fe25Srmesta /* 615*8200fe25Srmesta * Non-Recoverable error occurred. No sense 616*8200fe25Srmesta * in keep pinging the DNS server at this 617*8200fe25Srmesta * point, so we disable any further contact. 618*8200fe25Srmesta */ 619*8200fe25Srmesta #ifdef DEBUG 620*8200fe25Srmesta syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami); 621*8200fe25Srmesta #endif 622*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 623*8200fe25Srmesta s_dns_disabled = TRUE; 624*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 625*8200fe25Srmesta goto thr_reset; 626*8200fe25Srmesta 627*8200fe25Srmesta case HOST_NOT_FOUND: 628*8200fe25Srmesta /* 629*8200fe25Srmesta * Authoritative NS not responding... 630*8200fe25Srmesta * keep trying for non-authoritative reply 631*8200fe25Srmesta */ 632*8200fe25Srmesta /*FALLTHROUGH*/ 633*8200fe25Srmesta 634*8200fe25Srmesta case TRY_AGAIN: 635*8200fe25Srmesta /* keep trying */ 636*8200fe25Srmesta #ifdef DEBUG 637*8200fe25Srmesta syslog(LOG_ERR, "%s: retrying...", whoami); 638*8200fe25Srmesta #endif 639*8200fe25Srmesta break; 640*8200fe25Srmesta 641*8200fe25Srmesta case NETDB_INTERNAL: 642*8200fe25Srmesta default: 643*8200fe25Srmesta #ifdef DEBUG 644*8200fe25Srmesta syslog(LOG_ERR, "%s: Internal resolver error: %s", 645*8200fe25Srmesta whoami, strerror(errno)); 646*8200fe25Srmesta #endif 647*8200fe25Srmesta goto thr_reset; 648*8200fe25Srmesta } 649*8200fe25Srmesta } 650*8200fe25Srmesta thr_reset: 651*8200fe25Srmesta (void) rw_wrlock(&s_dns_data_lock); 652*8200fe25Srmesta dns_txt_cached = 0; 653*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 654*8200fe25Srmesta resolv_txt_reset(); 655*8200fe25Srmesta 656*8200fe25Srmesta thr_okay: 657*8200fe25Srmesta /* mark thread as done */ 658*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 659*8200fe25Srmesta s_dns_qthr_created = FALSE; 660*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 661*8200fe25Srmesta 662*8200fe25Srmesta (void) thr_exit(NULL); 663*8200fe25Srmesta /*NOTREACHED*/ 664*8200fe25Srmesta return (NULL); 665*8200fe25Srmesta } 666*8200fe25Srmesta 667*8200fe25Srmesta /* 668*8200fe25Srmesta * nfsmapid's interface into the resolver for getting the TXT record. 669*8200fe25Srmesta * 670*8200fe25Srmesta * Key concepts: 671*8200fe25Srmesta * 672*8200fe25Srmesta * o If the DNS server is available and the TXT record is found, we 673*8200fe25Srmesta * simply decode the output and fill the exported dns_txt_domain 674*8200fe25Srmesta * global, so our caller can configure the daemon appropriately. 675*8200fe25Srmesta * 676*8200fe25Srmesta * o If the TXT record is not found, then having done resolv_txt_reset() 677*8200fe25Srmesta * first will allow our caller to recognize that the exported globals 678*8200fe25Srmesta * are empty and thus configure nfsmapid to use the default DNS domain. 679*8200fe25Srmesta * 680*8200fe25Srmesta * o Having no /etc/resolv.conf file is pretty much a show stopper, since 681*8200fe25Srmesta * there is no name server address information. We return since we've 682*8200fe25Srmesta * already have reset the TXT global state. 683*8200fe25Srmesta * 684*8200fe25Srmesta * o If a previous call to the DNS server resulted in an unrecoverable 685*8200fe25Srmesta * error, then we disable further contact to the DNS server and return. 686*8200fe25Srmesta * Having the TXT global state already reset guarantees that our caller 687*8200fe25Srmesta * will fall back to the right configuration. 688*8200fe25Srmesta * 689*8200fe25Srmesta * o Query thread creation is throttled by s_dns_qthr_created. We mitigate 690*8200fe25Srmesta * the problem of an already unresponsive DNS server by allowing at most 691*8200fe25Srmesta * 1 outstanding query thread since we could potentially have a substantial 692*8200fe25Srmesta * amount of clients hammering on the same DNS server attempting to get 693*8200fe25Srmesta * the TXT record. 694*8200fe25Srmesta */ 695*8200fe25Srmesta static void 696*8200fe25Srmesta get_dns_txt_domain(cb_t *argp) 697*8200fe25Srmesta { 698*8200fe25Srmesta int err; 699*8200fe25Srmesta #ifdef DEBUG 700*8200fe25Srmesta static uint64_t msg_done = 0; 701*8200fe25Srmesta char *whoami = "get_dns_txt_domain"; 702*8200fe25Srmesta #endif 703*8200fe25Srmesta long thr_flags = THR_DETACHED; 704*8200fe25Srmesta struct stat st; 705*8200fe25Srmesta 706*8200fe25Srmesta /* 707*8200fe25Srmesta * We reset TXT variables first in case /etc/resolv.conf 708*8200fe25Srmesta * is missing or we've had unrecoverable resolver errors, 709*8200fe25Srmesta * we'll default to get_dns_domain(). If a previous DNS 710*8200fe25Srmesta * TXT RR was found, don't clear it until we're certain 711*8200fe25Srmesta * that contact can be made to the DNS server (see block 712*8200fe25Srmesta * comment atop resolv_txt_reset). If we're responding to 713*8200fe25Srmesta * a SIGHUP signal, force a reset of the cached copy. 714*8200fe25Srmesta */ 715*8200fe25Srmesta if (argp && argp->signal) { 716*8200fe25Srmesta (void) rw_wrlock(&s_dns_data_lock); 717*8200fe25Srmesta dns_txt_cached = 0; 718*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 719*8200fe25Srmesta } 720*8200fe25Srmesta resolv_txt_reset(); 721*8200fe25Srmesta 722*8200fe25Srmesta errno = 0; 723*8200fe25Srmesta if (stat(_PATH_RESCONF, &st) < 0 && errno == ENOENT) { 724*8200fe25Srmesta /* 725*8200fe25Srmesta * If /etc/resolv.conf is not there, then we'll 726*8200fe25Srmesta * get the domain from domainname(1M). No real 727*8200fe25Srmesta * reason to query DNS or fire a thread since we 728*8200fe25Srmesta * have no nameserver addresses. 729*8200fe25Srmesta */ 730*8200fe25Srmesta goto txtclear; 731*8200fe25Srmesta } 732*8200fe25Srmesta 733*8200fe25Srmesta (void) rw_rdlock(&s_dns_impl_lock); 734*8200fe25Srmesta if (s_dns_disabled) { 735*8200fe25Srmesta /* 736*8200fe25Srmesta * If there were non-recoverable problems with DNS, 737*8200fe25Srmesta * we have stopped querying DNS entirely. See 738*8200fe25Srmesta * NO_RECOVERY clause below. 739*8200fe25Srmesta */ 740*8200fe25Srmesta #ifdef DEBUG 741*8200fe25Srmesta syslog(LOG_ERR, "%s: DNS queries disabled", whoami); 742*8200fe25Srmesta #endif 743*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 744*8200fe25Srmesta return; 745*8200fe25Srmesta } 746*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 747*8200fe25Srmesta 748*8200fe25Srmesta (void) resolv_init(); 749*8200fe25Srmesta switch (resolv_search()) { 750*8200fe25Srmesta case NETDB_SUCCESS: 751*8200fe25Srmesta /* 752*8200fe25Srmesta * If there _is_ a TXT record, we let 753*8200fe25Srmesta * our caller set the global state. 754*8200fe25Srmesta */ 755*8200fe25Srmesta resolv_decode(); 756*8200fe25Srmesta resolv_get_txt_data(); 757*8200fe25Srmesta return; 758*8200fe25Srmesta 759*8200fe25Srmesta case TRY_AGAIN: 760*8200fe25Srmesta if (argp == NULL || argp->fcn == NULL) 761*8200fe25Srmesta /* 762*8200fe25Srmesta * If no valid argument was passed or 763*8200fe25Srmesta * callback defined, don't fire thread 764*8200fe25Srmesta */ 765*8200fe25Srmesta return; 766*8200fe25Srmesta 767*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 768*8200fe25Srmesta if (s_dns_qthr_created) { 769*8200fe25Srmesta /* 770*8200fe25Srmesta * We may have lots of clients, so we don't 771*8200fe25Srmesta * want to bog down the DNS server with tons 772*8200fe25Srmesta * of requests... lest it becomes even more 773*8200fe25Srmesta * unresponsive, so limit 1 thread to query 774*8200fe25Srmesta * DNS at a time. 775*8200fe25Srmesta */ 776*8200fe25Srmesta #ifdef DEBUG 777*8200fe25Srmesta syslog(LOG_ERR, "%s: query thread already active", 778*8200fe25Srmesta whoami); 779*8200fe25Srmesta #endif 780*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 781*8200fe25Srmesta return; 782*8200fe25Srmesta } 783*8200fe25Srmesta 784*8200fe25Srmesta /* 785*8200fe25Srmesta * DNS did not respond ! Set timeout and kick off 786*8200fe25Srmesta * thread to try op again after s_dns_tout seconds. 787*8200fe25Srmesta * We've made sure that we don't have an already 788*8200fe25Srmesta * running thread above. 789*8200fe25Srmesta */ 790*8200fe25Srmesta s_dns_tout = NFSMAPID_DNS_TOUT_SECS; 791*8200fe25Srmesta err = thr_create(NULL, 0, resolv_query_thread, (void *)argp, 792*8200fe25Srmesta thr_flags, &s_dns_qthread); 793*8200fe25Srmesta if (!err) { 794*8200fe25Srmesta s_dns_qthr_created = TRUE; 795*8200fe25Srmesta } 796*8200fe25Srmesta #ifdef DEBUG 797*8200fe25Srmesta else { 798*8200fe25Srmesta msg_done++; 799*8200fe25Srmesta if (!(msg_done % NFSMAPID_SLOG_RATE)) 800*8200fe25Srmesta syslog(LOG_ERR, EMSG_DNS_THREAD_ERROR); 801*8200fe25Srmesta } 802*8200fe25Srmesta #endif 803*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 804*8200fe25Srmesta return; 805*8200fe25Srmesta 806*8200fe25Srmesta case NO_RECOVERY: 807*8200fe25Srmesta #ifdef DEBUG 808*8200fe25Srmesta syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami); 809*8200fe25Srmesta #endif 810*8200fe25Srmesta (void) rw_wrlock(&s_dns_impl_lock); 811*8200fe25Srmesta s_dns_disabled = TRUE; 812*8200fe25Srmesta (void) rw_unlock(&s_dns_impl_lock); 813*8200fe25Srmesta 814*8200fe25Srmesta /*FALLTHROUGH*/ 815*8200fe25Srmesta 816*8200fe25Srmesta default: 817*8200fe25Srmesta /* 818*8200fe25Srmesta * For any other errors... DNS is responding, but 819*8200fe25Srmesta * either it has no data, or some other problem is 820*8200fe25Srmesta * occuring. At any rate, the TXT domain should not 821*8200fe25Srmesta * be used, so we default to the DNS domain. 822*8200fe25Srmesta */ 823*8200fe25Srmesta break; 824*8200fe25Srmesta } 825*8200fe25Srmesta 826*8200fe25Srmesta txtclear: 827*8200fe25Srmesta (void) rw_wrlock(&s_dns_data_lock); 828*8200fe25Srmesta dns_txt_cached = 0; 829*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 830*8200fe25Srmesta resolv_txt_reset(); 831*8200fe25Srmesta } 832*8200fe25Srmesta 833*8200fe25Srmesta static int 834*8200fe25Srmesta get_mtime(const char *fname, timestruc_t *mtim) 835*8200fe25Srmesta { 836*8200fe25Srmesta struct stat st; 837*8200fe25Srmesta int err; 838*8200fe25Srmesta 839*8200fe25Srmesta if ((err = stat(fname, &st)) != 0) 840*8200fe25Srmesta return (err); 841*8200fe25Srmesta 842*8200fe25Srmesta *mtim = st.st_mtim; 843*8200fe25Srmesta return (0); 844*8200fe25Srmesta } 845*8200fe25Srmesta 846*8200fe25Srmesta 847*8200fe25Srmesta /* 848*8200fe25Srmesta * trim_wspace is a destructive interface; it is up to 849*8200fe25Srmesta * the caller to save off an original copy if needed. 850*8200fe25Srmesta */ 851*8200fe25Srmesta static char * 852*8200fe25Srmesta trim_wspace(char *dp) 853*8200fe25Srmesta { 854*8200fe25Srmesta char *r; 855*8200fe25Srmesta char *ndp; 856*8200fe25Srmesta 857*8200fe25Srmesta /* 858*8200fe25Srmesta * Any empty domain is not valid 859*8200fe25Srmesta */ 860*8200fe25Srmesta if (dp == NULL) 861*8200fe25Srmesta return (NULL); 862*8200fe25Srmesta 863*8200fe25Srmesta /* 864*8200fe25Srmesta * Skip leading blanks 865*8200fe25Srmesta */ 866*8200fe25Srmesta for (ndp = dp; *ndp != '\0'; ndp++) { 867*8200fe25Srmesta if (!isspace(*ndp)) 868*8200fe25Srmesta break; 869*8200fe25Srmesta } 870*8200fe25Srmesta 871*8200fe25Srmesta /* 872*8200fe25Srmesta * If we reached the end of the string w/o 873*8200fe25Srmesta * finding a non-blank char, return error 874*8200fe25Srmesta */ 875*8200fe25Srmesta if (*ndp == '\0') 876*8200fe25Srmesta return (NULL); 877*8200fe25Srmesta 878*8200fe25Srmesta /* 879*8200fe25Srmesta * Find next blank in string 880*8200fe25Srmesta */ 881*8200fe25Srmesta for (r = ndp; *r != '\0'; r++) { 882*8200fe25Srmesta if (isspace(*r)) 883*8200fe25Srmesta break; 884*8200fe25Srmesta } 885*8200fe25Srmesta 886*8200fe25Srmesta /* 887*8200fe25Srmesta * No more blanks found, we are done 888*8200fe25Srmesta */ 889*8200fe25Srmesta if (*r == '\0') 890*8200fe25Srmesta return (ndp); 891*8200fe25Srmesta 892*8200fe25Srmesta /* 893*8200fe25Srmesta * Terminate string at blank 894*8200fe25Srmesta */ 895*8200fe25Srmesta *r++ = '\0'; 896*8200fe25Srmesta 897*8200fe25Srmesta /* 898*8200fe25Srmesta * Skip any trailing spaces 899*8200fe25Srmesta */ 900*8200fe25Srmesta while (*r != '\0') { 901*8200fe25Srmesta /* 902*8200fe25Srmesta * If a non-blank is found, it is an 903*8200fe25Srmesta * illegal domain (embedded blanks). 904*8200fe25Srmesta */ 905*8200fe25Srmesta if (!isspace(*r)) 906*8200fe25Srmesta return (NULL); 907*8200fe25Srmesta r++; 908*8200fe25Srmesta } 909*8200fe25Srmesta return (ndp); 910*8200fe25Srmesta } 911*8200fe25Srmesta 912*8200fe25Srmesta static void 913*8200fe25Srmesta get_nfs_domain(void) 914*8200fe25Srmesta { 915*8200fe25Srmesta char *ndomain; 916*8200fe25Srmesta timestruc_t ntime; 917*8200fe25Srmesta 918*8200fe25Srmesta /* 919*8200fe25Srmesta * If we can't get stats for the config file, then 920*8200fe25Srmesta * zap the NFS domain info. If mtime hasn't changed, 921*8200fe25Srmesta * then there's no work to do, so just return. 922*8200fe25Srmesta */ 923*8200fe25Srmesta if (get_mtime(NFSADMIN, &ntime) != 0) { 924*8200fe25Srmesta ZAP_DOMAIN(nfs); 925*8200fe25Srmesta return; 926*8200fe25Srmesta } 927*8200fe25Srmesta 928*8200fe25Srmesta if (TIMESTRUC_EQ(ntime, nfs_mtime)) 929*8200fe25Srmesta return; 930*8200fe25Srmesta 931*8200fe25Srmesta /* 932*8200fe25Srmesta * Get NFSMAPID_DOMAIN value from /etc/default/nfs for now. 933*8200fe25Srmesta * Note: defread() returns a ptr to TSD. 934*8200fe25Srmesta */ 935*8200fe25Srmesta if (defopen(NFSADMIN) == 0) { 936*8200fe25Srmesta char *dp = NULL; 937*8200fe25Srmesta #ifdef DEBUG 938*8200fe25Srmesta char *whoami = "get_nfs_domain"; 939*8200fe25Srmesta char orig[NS_MAXCDNAME] = {0}; 940*8200fe25Srmesta #endif 941*8200fe25Srmesta ndomain = (char *)defread("NFSMAPID_DOMAIN="); 942*8200fe25Srmesta (void) defopen(NULL); 943*8200fe25Srmesta #ifdef DEBUG 944*8200fe25Srmesta if (ndomain) 945*8200fe25Srmesta (void) strncpy(orig, ndomain, NS_MAXCDNAME); 946*8200fe25Srmesta #endif 947*8200fe25Srmesta /* 948*8200fe25Srmesta * NFSMAPID_DOMAIN was set, so it's time for validation. If 949*8200fe25Srmesta * it's okay, then update NFS domain and return. If not, 950*8200fe25Srmesta * bail (syslog in DEBUG). We make nfsmapid more a bit 951*8200fe25Srmesta * more forgiving of trailing and leading white space. 952*8200fe25Srmesta */ 953*8200fe25Srmesta if ((dp = trim_wspace(ndomain)) != NULL) { 954*8200fe25Srmesta if (mapid_stdchk_domain(dp) > 0) { 955*8200fe25Srmesta nfs_domain_len = strlen(dp); 956*8200fe25Srmesta (void) strncpy(nfs_domain, dp, NS_MAXCDNAME); 957*8200fe25Srmesta nfs_domain[NS_MAXCDNAME] = '\0'; 958*8200fe25Srmesta nfs_mtime = ntime; 959*8200fe25Srmesta return; 960*8200fe25Srmesta } 961*8200fe25Srmesta } 962*8200fe25Srmesta #ifdef DEBUG 963*8200fe25Srmesta if (orig[0] != '\0') { 964*8200fe25Srmesta syslog(LOG_ERR, gettext("%s: Invalid domain name \"%s\"" 965*8200fe25Srmesta " found in configuration file."), whoami, orig); 966*8200fe25Srmesta } 967*8200fe25Srmesta #endif 968*8200fe25Srmesta } 969*8200fe25Srmesta 970*8200fe25Srmesta /* 971*8200fe25Srmesta * So the NFS config file changed but it couldn't be opened or 972*8200fe25Srmesta * it didn't specify NFSMAPID_DOMAIN or it specified an invalid 973*8200fe25Srmesta * NFSMAPID_DOMAIN. Time to zap current NFS domain info. 974*8200fe25Srmesta */ 975*8200fe25Srmesta ZAP_DOMAIN(nfs); 976*8200fe25Srmesta } 977*8200fe25Srmesta 978*8200fe25Srmesta static void 979*8200fe25Srmesta get_dns_domain(void) 980*8200fe25Srmesta { 981*8200fe25Srmesta timestruc_t ntime = {0}; 982*8200fe25Srmesta 983*8200fe25Srmesta /* 984*8200fe25Srmesta * If we can't get stats for the config file, then 985*8200fe25Srmesta * zap the DNS domain info. If mtime hasn't changed, 986*8200fe25Srmesta * then there's no work to do, so just return. 987*8200fe25Srmesta */ 988*8200fe25Srmesta errno = 0; 989*8200fe25Srmesta if (get_mtime(_PATH_RESCONF, &ntime) != 0) { 990*8200fe25Srmesta switch (errno) { 991*8200fe25Srmesta case ENOENT: 992*8200fe25Srmesta /* 993*8200fe25Srmesta * The resolver defaults to obtaining the 994*8200fe25Srmesta * domain off of the NIS domainname(1M) if 995*8200fe25Srmesta * /etc/resolv.conf does not exist, so we 996*8200fe25Srmesta * move forward. 997*8200fe25Srmesta */ 998*8200fe25Srmesta break; 999*8200fe25Srmesta 1000*8200fe25Srmesta default: 1001*8200fe25Srmesta ZAP_DOMAIN(dns); 1002*8200fe25Srmesta return; 1003*8200fe25Srmesta } 1004*8200fe25Srmesta } else if (TIMESTRUC_EQ(ntime, dns_mtime)) 1005*8200fe25Srmesta return; 1006*8200fe25Srmesta 1007*8200fe25Srmesta /* 1008*8200fe25Srmesta * Re-initialize resolver to zap DNS domain from previous 1009*8200fe25Srmesta * resolv_init() calls. 1010*8200fe25Srmesta */ 1011*8200fe25Srmesta (void) resolv_init(); 1012*8200fe25Srmesta 1013*8200fe25Srmesta /* 1014*8200fe25Srmesta * Update cached DNS domain. No need for validation since 1015*8200fe25Srmesta * domain comes from resolver. If resolver doesn't return the 1016*8200fe25Srmesta * domain, then zap the DNS domain. This shouldn't ever happen, 1017*8200fe25Srmesta * and if it does, the machine has bigger problems (so no need 1018*8200fe25Srmesta * to generate a message that says DNS appears to be broken). 1019*8200fe25Srmesta */ 1020*8200fe25Srmesta (void) rw_rdlock(&s_dns_data_lock); 1021*8200fe25Srmesta if (sysdns_domain[0] != '\0') { 1022*8200fe25Srmesta (void) strncpy(dns_domain, sysdns_domain, NS_MAXCDNAME); 1023*8200fe25Srmesta dns_domain_len = strlen(sysdns_domain); 1024*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 1025*8200fe25Srmesta dns_mtime = ntime; 1026*8200fe25Srmesta return; 1027*8200fe25Srmesta } 1028*8200fe25Srmesta (void) rw_unlock(&s_dns_data_lock); 1029*8200fe25Srmesta 1030*8200fe25Srmesta ZAP_DOMAIN(dns); 1031*8200fe25Srmesta } 1032*8200fe25Srmesta 1033*8200fe25Srmesta /* 1034*8200fe25Srmesta * PSARC 2005/487 Contracted Sun Private Interface 1035*8200fe25Srmesta * mapid_stdchk_domain() 1036*8200fe25Srmesta * Changes must be reviewed by Solaris File Sharing 1037*8200fe25Srmesta * Changes must be communicated to contract-2005-487-01@sun.com 1038*8200fe25Srmesta * 1039*8200fe25Srmesta * Based on the recommendations from RFC1033 and RFC1035, check 1040*8200fe25Srmesta * if a given domain name string is valid. Return values are: 1041*8200fe25Srmesta * 1042*8200fe25Srmesta * 1 = valid domain name 1043*8200fe25Srmesta * 0 = invalid domain name (or invalid embedded character) 1044*8200fe25Srmesta * -1 = domain length > NS_MAXCDNAME 1045*8200fe25Srmesta */ 1046*8200fe25Srmesta int 1047*8200fe25Srmesta mapid_stdchk_domain(const char *ds) 1048*8200fe25Srmesta { 1049*8200fe25Srmesta int i; 1050*8200fe25Srmesta size_t len; 1051*8200fe25Srmesta 1052*8200fe25Srmesta if (ds[0] == '\0') 1053*8200fe25Srmesta return (0); 1054*8200fe25Srmesta else 1055*8200fe25Srmesta len = strlen(ds) - 1; 1056*8200fe25Srmesta 1057*8200fe25Srmesta /* 1058*8200fe25Srmesta * 1st char _must_ be alphabetic char _AND_ last char _must_ 1059*8200fe25Srmesta * be alphanumeric. We check for other valid chars below. 1060*8200fe25Srmesta */ 1061*8200fe25Srmesta if (!isalpha(ds[0]) || !isalpha(ds[len]) && !isdigit(ds[len])) 1062*8200fe25Srmesta return (0); 1063*8200fe25Srmesta 1064*8200fe25Srmesta for (i = 0; *ds && i <= NS_MAXCDNAME; i++, ds++) { 1065*8200fe25Srmesta if (!isalpha(*ds) && !isdigit(*ds) && 1066*8200fe25Srmesta (*ds != '.') && (*ds != '-') && (*ds != '_')) 1067*8200fe25Srmesta return (0); 1068*8200fe25Srmesta } 1069*8200fe25Srmesta return (i == (NS_MAXCDNAME + 1) ? -1 : 1); 1070*8200fe25Srmesta } 1071*8200fe25Srmesta 1072*8200fe25Srmesta /* 1073*8200fe25Srmesta * PSARC 2005/487 Consolidation Private 1074*8200fe25Srmesta * mapid_reeval_domain() 1075*8200fe25Srmesta * Changes must be reviewed by Solaris File Sharing 1076*8200fe25Srmesta */ 1077*8200fe25Srmesta void 1078*8200fe25Srmesta mapid_reeval_domain(cb_t *arg) 1079*8200fe25Srmesta { 1080*8200fe25Srmesta char *domain = NULL; 1081*8200fe25Srmesta 1082*8200fe25Srmesta get_nfs_domain(); 1083*8200fe25Srmesta if (nfs_domain_len != 0) { 1084*8200fe25Srmesta domain = nfs_domain; 1085*8200fe25Srmesta goto dsync; 1086*8200fe25Srmesta } 1087*8200fe25Srmesta 1088*8200fe25Srmesta get_dns_txt_domain(arg); 1089*8200fe25Srmesta if (dns_txt_domain_len != 0) 1090*8200fe25Srmesta domain = dns_txt_domain; 1091*8200fe25Srmesta else { 1092*8200fe25Srmesta /* 1093*8200fe25Srmesta * We're either here because: 1094*8200fe25Srmesta * 1095*8200fe25Srmesta * . NFSMAPID_DOMAIN was not set in /etc/default/nfs 1096*8200fe25Srmesta * . No suitable DNS TXT resource record exists 1097*8200fe25Srmesta * . DNS server is not responding to requests 1098*8200fe25Srmesta * 1099*8200fe25Srmesta * in either case, we want to default to using the 1100*8200fe25Srmesta * system configured DNS domain. If this fails, then 1101*8200fe25Srmesta * dns_domain will be empty and dns_domain_len will 1102*8200fe25Srmesta * be 0. 1103*8200fe25Srmesta */ 1104*8200fe25Srmesta get_dns_domain(); 1105*8200fe25Srmesta domain = dns_domain; 1106*8200fe25Srmesta } 1107*8200fe25Srmesta 1108*8200fe25Srmesta dsync: 1109*8200fe25Srmesta domain_sync(arg, domain); 1110*8200fe25Srmesta } 1111*8200fe25Srmesta 1112*8200fe25Srmesta /* 1113*8200fe25Srmesta * PSARC 2005/487 Consolidation Private 1114*8200fe25Srmesta * mapid_get_domain() 1115*8200fe25Srmesta * Changes must be reviewed by Solaris File Sharing 1116*8200fe25Srmesta * 1117*8200fe25Srmesta * The use of TSD in mapid_get_domain() diverges slightly from the typical 1118*8200fe25Srmesta * TSD use, since here, the benefit of doing TSD is mostly to allocate 1119*8200fe25Srmesta * a per-thread buffer that will be utilized by other up-calls to the 1120*8200fe25Srmesta * daemon. 1121*8200fe25Srmesta * 1122*8200fe25Srmesta * In doors, the thread used for the upcall never really exits, hence 1123*8200fe25Srmesta * the typical destructor function defined via thr_keycreate() will 1124*8200fe25Srmesta * never be called. Thus, we only use TSD to allocate the per-thread 1125*8200fe25Srmesta * buffer and fill it up w/the configured 'mapid_domain' on each call. 1126*8200fe25Srmesta * This still alleviates the problem of having the caller free any 1127*8200fe25Srmesta * malloc'd space. 1128*8200fe25Srmesta */ 1129*8200fe25Srmesta char * 1130*8200fe25Srmesta mapid_get_domain(void) 1131*8200fe25Srmesta { 1132*8200fe25Srmesta void *tsd = NULL; 1133*8200fe25Srmesta 1134*8200fe25Srmesta (void) thr_getspecific(s_thr_key, &tsd); 1135*8200fe25Srmesta if (tsd == NULL) { 1136*8200fe25Srmesta tsd = malloc(NS_MAXCDNAME+1); 1137*8200fe25Srmesta if (tsd != NULL) { 1138*8200fe25Srmesta (void) rw_rdlock(&mapid_domain_lock); 1139*8200fe25Srmesta (void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME); 1140*8200fe25Srmesta (void) rw_unlock(&mapid_domain_lock); 1141*8200fe25Srmesta (void) thr_setspecific(s_thr_key, tsd); 1142*8200fe25Srmesta } 1143*8200fe25Srmesta } else { 1144*8200fe25Srmesta (void) rw_rdlock(&mapid_domain_lock); 1145*8200fe25Srmesta (void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME); 1146*8200fe25Srmesta (void) rw_unlock(&mapid_domain_lock); 1147*8200fe25Srmesta } 1148*8200fe25Srmesta return ((char *)tsd); 1149*8200fe25Srmesta } 1150*8200fe25Srmesta 1151*8200fe25Srmesta /* 1152*8200fe25Srmesta * PSARC 2005/487 Contracted Sun Private Interface 1153*8200fe25Srmesta * mapid_derive_domain() 1154*8200fe25Srmesta * Changes must be reviewed by Solaris File Sharing 1155*8200fe25Srmesta * Changes must be communicated to contract-2005-487-01@sun.com 1156*8200fe25Srmesta * 1157*8200fe25Srmesta * This interface is called solely via sysidnfs4 iff no 1158*8200fe25Srmesta * NFSMAPID_DOMAIN was found. So, there is no ill effect 1159*8200fe25Srmesta * of having the reeval function call get_nfs_domain(). 1160*8200fe25Srmesta */ 1161*8200fe25Srmesta char * 1162*8200fe25Srmesta mapid_derive_domain(void) 1163*8200fe25Srmesta { 1164*8200fe25Srmesta cb_t cb = {0}; 1165*8200fe25Srmesta 1166*8200fe25Srmesta _lib_init(); 1167*8200fe25Srmesta mapid_reeval_domain(&cb); 1168*8200fe25Srmesta return (mapid_get_domain()); 1169*8200fe25Srmesta } 1170*8200fe25Srmesta 1171*8200fe25Srmesta void 1172*8200fe25Srmesta _lib_init(void) 1173*8200fe25Srmesta { 1174*8200fe25Srmesta (void) resolv_init(); 1175*8200fe25Srmesta (void) rwlock_init(&mapid_domain_lock, USYNC_THREAD, NULL); 1176*8200fe25Srmesta (void) thr_keycreate(&s_thr_key, NULL); 1177*8200fe25Srmesta lib_init_done++; 1178*8200fe25Srmesta } 1179