/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ns_sldap.h" #include "ns_internal.h" #include "ns_cache_door.h" #define UDP "/dev/udp" #define MAXIFS 32 struct ifinfo { struct in_addr addr, netmask; }; static ns_service_map ns_def_map[] = { { "passwd", "ou=people,", NULL }, { "shadow", "ou=people,", "passwd" }, { "user_attr", "ou=people,", "passwd" }, { "audit_user", "ou=people,", "passwd" }, { "group", "ou=group,", NULL }, { "rpc", "ou=rpc,", NULL }, { "project", "ou=projects,", NULL }, { "protocols", "ou=protocols,", NULL }, { "networks", "ou=networks,", NULL }, { "netmasks", "ou=networks,", "networks" }, { "netgroup", "ou=netgroup,", NULL }, { "aliases", "ou=aliases,", NULL }, { "Hosts", "ou=Hosts,", NULL }, { "ipnodes", "ou=Hosts,", "hosts" }, { "Services", "ou=Services,", NULL }, { "bootparams", "ou=ethers,", "ethers" }, { "ethers", "ou=ethers,", NULL }, { "auth_attr", "ou=SolarisAuthAttr,", NULL }, { "prof_attr", "ou=SolarisProfAttr,", NULL }, { "exec_attr", "ou=SolarisProfAttr,", "prof_attr" }, { "profile", "ou=profile,", NULL }, { "printers", "ou=printers,", NULL }, { "automount", "", NULL }, { "tnrhtp", "ou=ipTnet,", NULL }, { "tnrhdb", "ou=ipTnet,", "tnrhtp" }, { NULL, NULL, NULL } }; static char ** parseDN(const char *val, const char *service); static char ** sortServerNet(char **srvlist); static char ** sortServerPref(char **srvlist, char **preflist, boolean_t flag, int version, int *error); /* * FUNCTION: s_api_printResult * Given a ns_ldap_result structure print it. */ int __s_api_printResult(ns_ldap_result_t *result) { ns_ldap_entry_t *curEntry; int i, j, k = 0; #ifdef DEBUG (void) fprintf(stderr, "__s_api_printResult START\n"); #endif (void) printf("--------------------------------------\n"); if (result == NULL) { (void) printf("No result\n"); return (0); } (void) printf("entries_count %d\n", result->entries_count); curEntry = result->entry; for (i = 0; i < result->entries_count; i++) { (void) printf("entry %d has attr_count = %d \n", i, curEntry->attr_count); for (j = 0; j < curEntry->attr_count; j++) { (void) printf("entry %d has attr_pair[%d] = %s \n", i, j, curEntry->attr_pair[j]->attrname); for (k = 0; k < 20 && curEntry->attr_pair[j]->attrvalue[k]; k++) (void) printf("entry %d has attr_pair[%d]->" "attrvalue[%d] = %s \n", i, j, k, curEntry->attr_pair[j]->attrvalue[k]); } (void) printf("\n--------------------------------------\n"); curEntry = curEntry->next; } return (1); } /* * FUNCTION: __s_api_getSearchScope * * Retrieve the search scope for ldap search from the config module. * * RETURN VALUES: NS_LDAP_SUCCESS, NS_LDAP_CONFIG * INPUT: NONE * OUTPUT: searchScope, errorp */ int __s_api_getSearchScope( int *searchScope, ns_ldap_error_t **errorp) { char errmsg[MAXERROR]; void **paramVal = NULL; int rc = 0; int scope = 0; #ifdef DEBUG (void) fprintf(stderr, "__s_api_getSearchScope START\n"); #endif if (*searchScope == 0) { if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_SCOPE_P, ¶mVal, errorp)) != NS_LDAP_SUCCESS) { return (rc); } if (paramVal && *paramVal) scope = * (int *)(*paramVal); else scope = NS_LDAP_SCOPE_ONELEVEL; (void) __ns_ldap_freeParam(¶mVal); } else { scope = *searchScope; } switch (scope) { case NS_LDAP_SCOPE_ONELEVEL: *searchScope = LDAP_SCOPE_ONELEVEL; break; case NS_LDAP_SCOPE_BASE: *searchScope = LDAP_SCOPE_BASE; break; case NS_LDAP_SCOPE_SUBTREE: *searchScope = LDAP_SCOPE_SUBTREE; break; default: (void) snprintf(errmsg, sizeof (errmsg), gettext("Invalid search scope!")); MKERROR(LOG_ERR, *errorp, NS_CONFIG_FILE, strdup(errmsg), NS_LDAP_CONFIG); return (NS_LDAP_CONFIG); } return (NS_LDAP_SUCCESS); } /* * FUNCTION: __ns_ldap_dupAuth * * Duplicates an authentication structure. * * RETURN VALUES: copy of authp or NULL on error * INPUT: authp */ ns_cred_t * __ns_ldap_dupAuth(const ns_cred_t *authp) { ns_cred_t *ap; #ifdef DEBUG (void) fprintf(stderr, "__ns_ldap_dupAuth START\n"); #endif if (authp == NULL) return (NULL); ap = (ns_cred_t *)calloc(1, sizeof (ns_cred_t)); if (ap == NULL) return (NULL); if (authp->hostcertpath) { ap->hostcertpath = strdup(authp->hostcertpath); if (ap->hostcertpath == NULL) { free(ap); return (NULL); } } if (authp->cred.unix_cred.userID) { ap->cred.unix_cred.userID = strdup(authp->cred.unix_cred.userID); if (ap->cred.unix_cred.userID == NULL) { (void) __ns_ldap_freeCred(&ap); return (NULL); } } if (authp->cred.unix_cred.passwd) { ap->cred.unix_cred.passwd = strdup(authp->cred.unix_cred.passwd); if (ap->cred.unix_cred.passwd == NULL) { (void) __ns_ldap_freeCred(&ap); return (NULL); } } if (authp->cred.cert_cred.nickname) { ap->cred.cert_cred.nickname = strdup(authp->cred.cert_cred.nickname); if (ap->cred.cert_cred.nickname == NULL) { (void) __ns_ldap_freeCred(&ap); return (NULL); } } ap->auth.type = authp->auth.type; ap->auth.tlstype = authp->auth.tlstype; ap->auth.saslmech = authp->auth.saslmech; ap->auth.saslopt = authp->auth.saslopt; return (ap); } /* * FUNCTION: __ns_ldap_freeUnixCred * * Frees all the memory associated with a UnixCred_t structure. * * RETURN VALUES: NS_LDAP_INVALID_PARAM, NS_LDAP_SUCCESS * INPUT: UnixCred */ int __ns_ldap_freeUnixCred(UnixCred_t ** credp) { UnixCred_t *ap; #ifdef DEBUG (void) fprintf(stderr, "__ns_ldap_freeUnixCred START\n"); #endif if (credp == NULL || *credp == NULL) return (NS_LDAP_INVALID_PARAM); ap = *credp; if (ap->userID) { (void) memset(ap->userID, 0, strlen(ap->userID)); free(ap->userID); } if (ap->passwd) { (void) memset(ap->passwd, 0, strlen(ap->passwd)); free(ap->passwd); } free(ap); *credp = NULL; return (NS_LDAP_SUCCESS); } /* * FUNCTION: __ns_ldap_freeCred * * Frees all the memory associated with a ns_cred_t structure. * * RETURN VALUES: NS_LDAP_INVALID_PARAM, NS_LDAP_SUCCESS * INPUT: ns_cred_t */ int __ns_ldap_freeCred(ns_cred_t ** credp) { ns_cred_t *ap; #ifdef DEBUG (void) fprintf(stderr, "__ns_ldap_freeCred START\n"); #endif if (credp == NULL || *credp == NULL) return (NS_LDAP_INVALID_PARAM); ap = *credp; if (ap->hostcertpath) { (void) memset(ap->hostcertpath, 0, strlen(ap->hostcertpath)); free(ap->hostcertpath); } if (ap->cred.unix_cred.userID) { (void) memset(ap->cred.unix_cred.userID, 0, strlen(ap->cred.unix_cred.userID)); free(ap->cred.unix_cred.userID); } if (ap->cred.unix_cred.passwd) { (void) memset(ap->cred.unix_cred.passwd, 0, strlen(ap->cred.unix_cred.passwd)); free(ap->cred.unix_cred.passwd); } if (ap->cred.cert_cred.nickname) { (void) memset(ap->cred.cert_cred.nickname, 0, strlen(ap->cred.cert_cred.nickname)); free(ap->cred.cert_cred.nickname); } free(ap); *credp = NULL; return (NS_LDAP_SUCCESS); } /* * FUNCTION: __s_api_is_auth_matched * * Compare an authentication structure. * * RETURN VALUES: B_TRUE if matched, B_FALSE otherwise. * INPUT: auth1, auth2 */ boolean_t __s_api_is_auth_matched(const ns_cred_t *auth1, const ns_cred_t *auth2) { if ((auth1->auth.type != auth2->auth.type) || (auth1->auth.tlstype != auth2->auth.tlstype) || (auth1->auth.saslmech != auth2->auth.saslmech) || (auth1->auth.saslopt != auth2->auth.saslopt)) return (B_FALSE); if ((((auth1->auth.type == NS_LDAP_AUTH_SASL) && ((auth1->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) || (auth1->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) || (auth1->auth.type == NS_LDAP_AUTH_SIMPLE)) && ((auth1->cred.unix_cred.userID == NULL) || (auth1->cred.unix_cred.passwd == NULL) || ((strcasecmp(auth1->cred.unix_cred.userID, auth2->cred.unix_cred.userID) != 0)) || ((strcmp(auth1->cred.unix_cred.passwd, auth2->cred.unix_cred.passwd) != 0)))) return (B_FALSE); return (B_TRUE); } /* * FUNCTION: __s_api_getDNs * * Retrieves the default base dn for the given * service. * * RETURN VALUES: NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_CONFIG * INPUT: service * OUTPUT: DN, error */ typedef int (*pf)(const char *, char **, ns_ldap_error_t **); int __s_api_getDNs( char *** DN, const char *service, ns_ldap_error_t ** error) { void **paramVal = NULL; char **dns = NULL; int rc = 0; int i, len; pf prepend_auto2dn = __s_api_prepend_automountmapname_to_dn; #ifdef DEBUG (void) fprintf(stderr, "__s_api_getDNs START\n"); #endif if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_BASEDN_P, ¶mVal, error)) != NS_LDAP_SUCCESS) { return (rc); } if (!paramVal) { char errmsg[MAXERROR]; (void) snprintf(errmsg, sizeof (errmsg), gettext("BaseDN not defined")); MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errmsg), NS_LDAP_CONFIG); return (NS_LDAP_CONFIG); } dns = (char **)calloc(2, sizeof (char *)); if (dns == NULL) { (void) __ns_ldap_freeParam(¶mVal); return (NS_LDAP_MEMORY); } if (service == NULL) { dns[0] = strdup((char *)*paramVal); if (dns[0] == NULL) { (void) __ns_ldap_freeParam(¶mVal); free(dns); return (NS_LDAP_MEMORY); } } else { for (i = 0; ns_def_map[i].service != NULL; i++) { if (strcasecmp(service, ns_def_map[i].service) == 0) { len = strlen((char *)*paramVal) + strlen(ns_def_map[i].rdn) + 1; dns[0] = (char *) calloc(len, sizeof (char)); if (dns[0] == NULL) { (void) __ns_ldap_freeParam( ¶mVal); free(dns); return (NS_LDAP_MEMORY); } (void) strcpy(dns[0], ns_def_map[i].rdn); (void) strcat(dns[0], (char *)*paramVal); break; } } if (ns_def_map[i].service == NULL) { char *p = (char *)*paramVal; char *buffer = NULL; int buflen = 0; if (strchr(service, '=') == NULL) { /* automount entries */ if (strncasecmp(service, "auto_", 5) == 0) { buffer = strdup(p); if (!buffer) { free(dns); (void) __ns_ldap_freeParam( ¶mVal); return (NS_LDAP_MEMORY); } /* shorten name to avoid cstyle error */ rc = prepend_auto2dn( service, &buffer, error); if (rc != NS_LDAP_SUCCESS) { free(dns); free(buffer); (void) __ns_ldap_freeParam( ¶mVal); return (rc); } } else { /* strlen("nisMapName")+"="+","+'\0' = 13 */ buflen = strlen(service) + strlen(p) + 13; buffer = (char *)malloc(buflen); if (buffer == NULL) { free(dns); (void) __ns_ldap_freeParam( ¶mVal); return (NS_LDAP_MEMORY); } (void) snprintf(buffer, buflen, "nisMapName=%s,%s", service, p); } } else { buflen = strlen(service) + strlen(p) + 2; buffer = (char *)malloc(buflen); if (buffer == NULL) { free(dns); (void) __ns_ldap_freeParam(¶mVal); return (NS_LDAP_MEMORY); } (void) snprintf(buffer, buflen, "%s,%s", service, p); } dns[0] = buffer; } } (void) __ns_ldap_freeParam(¶mVal); *DN = dns; return (NS_LDAP_SUCCESS); } /* * FUNCTION: __s_api_get_search_DNs_v1 * * Retrieves the list of search DNS from the V1 profile for the given * service. * * RETURN VALUES: NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_CONFIG * INPUT: service * OUTPUT: DN, error */ int __s_api_get_search_DNs_v1( char *** DN, const char *service, ns_ldap_error_t ** error) { void **paramVal = NULL; void **temptr = NULL; char **dns = NULL; int rc = 0; if ((rc = __ns_ldap_getParam(NS_LDAP_SEARCH_DN_P, ¶mVal, error)) != NS_LDAP_SUCCESS) { return (rc); } if (service && paramVal) { for (temptr = paramVal; *temptr != NULL; temptr++) { dns = parseDN((const char *)(*temptr), (const char *)service); if (dns != NULL) break; } } (void) __ns_ldap_freeParam(¶mVal); *DN = dns; return (NS_LDAP_SUCCESS); } /* * FUNCTION: parseDN * * Parse a special formated list(val) into an array of char *. * * RETURN VALUE: A char * pointer to the new list of dns. * INPUT: val, service */ static char ** parseDN( const char *val, const char *service) { size_t len = 0; size_t slen = 0; char **retVal = NULL; const char *temptr; char *temptr2; const char *valend; int valNo = 0; int valSize = 0; int i; char *SSD_service = NULL; #ifdef DEBUG (void) fprintf(stderr, "parseDN START\n"); #endif if (val == NULL || *val == '\0') return (NULL); if (service == NULL || *service == '\0') return (NULL); len = strlen(val); slen = strlen(service); if (strncasecmp(val, service, slen) != 0) { /* * This routine is only called * to process V1 profile and * for V1 profile, map service * to the corresponding SSD_service * which is associated with a * real container in the LDAP directory * tree, e.g., map "shadow" to * "password". See function * __s_api_get_SSD_from_SSDtoUse_service * for similar service to SSD_service * mapping handling for V2 profile. */ for (i = 0; ns_def_map[i].service != NULL; i++) { if (ns_def_map[i].SSDtoUse_service && strcasecmp(service, ns_def_map[i].service) == 0) { SSD_service = ns_def_map[i].SSDtoUse_service; break; } } if (SSD_service == NULL) return (NULL); slen = strlen(SSD_service); if (strncasecmp(val, SSD_service, slen) != 0) return (NULL); } temptr = val + slen; while (*temptr == SPACETOK || *temptr == TABTOK) temptr++; if (*temptr != COLONTOK) return (NULL); while (*temptr) { temptr2 = strchr(temptr, OPARATOK); if (temptr2 == NULL) break; temptr2++; temptr2 = strchr(temptr2, CPARATOK); if (temptr2 == NULL) break; valNo++; temptr = temptr2+1; } retVal = (char **)calloc(valNo +1, sizeof (char *)); if (retVal == NULL) return (NULL); temptr = val; valend = val+len; for (i = 0; (i < valNo) && (temptr < valend); i++) { temptr = strchr(temptr, OPARATOK); if (temptr == NULL) { __s_api_free2dArray(retVal); return (NULL); } temptr++; temptr2 = strchr(temptr, CPARATOK); if (temptr2 == NULL) { __s_api_free2dArray(retVal); return (NULL); } valSize = temptr2 - temptr; retVal[i] = (char *)calloc(valSize + 1, sizeof (char)); if (retVal[i] == NULL) { __s_api_free2dArray(retVal); return (NULL); } (void) strncpy(retVal[i], temptr, valSize); retVal[i][valSize] = '\0'; temptr = temptr2 + 1; } return (retVal); } /* * __s_api_get_local_interfaces * * Returns a pointer to an array of addresses and netmasks of all interfaces * configured on the system. * * NOTE: This function is very IPv4 centric. */ static struct ifinfo * __s_api_get_local_interfaces() { struct ifconf ifc; struct ifreq ifreq, *ifr; struct ifinfo *localinfo; struct in_addr netmask; struct sockaddr_in *sin; void *buf = NULL; int fd = 0; int numifs = 0; int i, n = 0; if ((fd = open(UDP, O_RDONLY)) < 0) return ((struct ifinfo *)NULL); if (ioctl(fd, SIOCGIFNUM, (char *)&numifs) < 0) { numifs = MAXIFS; } buf = malloc(numifs * sizeof (struct ifreq)); if (buf == NULL) { (void) close(fd); return ((struct ifinfo *)NULL); } ifc.ifc_len = numifs * (int)sizeof (struct ifreq); ifc.ifc_buf = buf; if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0) { (void) close(fd); free(buf); buf = NULL; return ((struct ifinfo *)NULL); } ifr = (struct ifreq *)buf; numifs = ifc.ifc_len/(int)sizeof (struct ifreq); localinfo = (struct ifinfo *)malloc((numifs + 1) * sizeof (struct ifinfo)); if (localinfo == NULL) { (void) close(fd); free(buf); buf = NULL; return ((struct ifinfo *)NULL); } for (i = 0, n = numifs; n > 0; n--, ifr++) { uint_t ifrflags; ifreq = *ifr; if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) < 0) continue; ifrflags = ifreq.ifr_flags; if (((ifrflags & IFF_UP) == 0) || (ifr->ifr_addr.sa_family != AF_INET)) continue; if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifreq) < 0) continue; netmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; if (ioctl(fd, SIOCGIFADDR, (char *)&ifreq) < 0) continue; sin = (struct sockaddr_in *)&ifreq.ifr_addr; localinfo[i].addr = sin->sin_addr; localinfo[i].netmask = netmask; i++; } localinfo[i].addr.s_addr = 0; free(buf); buf = NULL; (void) close(fd); return (localinfo); } /* * __s_api_samenet(char *, struct ifinfo *) * * Returns 1 if address is on the same subnet of the array of addresses * passed in. * * NOTE: This function is only valid for IPv4 addresses. */ static int __s_api_IPv4sameNet(char *addr, struct ifinfo *ifs) { int answer = 0; if (addr && ifs) { char *addr_raw; unsigned long iaddr; int i; if ((addr_raw = strdup(addr)) != NULL) { char *s; /* Remove port number. */ if ((s = strchr(addr_raw, ':')) != NULL) *s = '\0'; iaddr = inet_addr(addr_raw); /* Loop through interface list to find match. */ for (i = 0; ifs[i].addr.s_addr != 0; i++) { if ((iaddr & ifs[i].netmask.s_addr) == (ifs[i].addr.s_addr & ifs[i].netmask.s_addr)) answer++; } free(addr_raw); } } return (answer); } /* * FUNCTION: __s_api_getServers * * Retrieve a list of ldap servers from the config module. * * RETURN VALUE: NS_LDAP_SUCCESS, NS_LDAP_CONFIG, NS_LDAP_MEMORY * INPUT: NONE * OUTPUT: servers, error */ int __s_api_getServers( char *** servers, ns_ldap_error_t ** error) { void **paramVal = NULL; char errmsg[MAXERROR]; char **sortServers = NULL; char **netservers = NULL; int rc = 0, err = NS_LDAP_CONFIG, version = 1; const char *str, *str1; #ifdef DEBUG (void) fprintf(stderr, "__s_api_getServers START\n"); #endif *servers = NULL; /* get profile version number */ if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, ¶mVal, error)) != NS_LDAP_SUCCESS) return (rc); if (paramVal == NULL || *paramVal == NULL) { (void) snprintf(errmsg, sizeof (errmsg), gettext("No file version")); MKERROR(LOG_INFO, *error, NS_CONFIG_FILE, strdup(errmsg), NS_LDAP_CONFIG); return (NS_LDAP_CONFIG); } if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0) version = 1; else if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_2) == 0) version = 2; (void) __ns_ldap_freeParam(¶mVal); paramVal = NULL; if ((rc = __ns_ldap_getParam(NS_LDAP_SERVERS_P, ¶mVal, error)) != NS_LDAP_SUCCESS) return (rc); /* * For version 2, default server list could be * empty. */ if ((paramVal == NULL || (char *)*paramVal == NULL) && version == 1) { str = NULL_OR_STR(__s_api_get_configname(NS_LDAP_SERVERS_P)); (void) snprintf(errmsg, sizeof (errmsg), gettext("Unable to retrieve the '%s' list"), str); MKERROR(LOG_WARNING, *error, NS_CONFIG_FILE, strdup(errmsg), NS_LDAP_CONFIG); return (NS_LDAP_CONFIG); } /* * Get server address(es) and go through them. */ *servers = (char **)paramVal; paramVal = NULL; /* Sort servers based on network. */ if (*servers) { netservers = sortServerNet(*servers); if (netservers) { free(*servers); *servers = netservers; } else { return (NS_LDAP_MEMORY); } } /* Get preferred server list and sort servers based on that. */ if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P, ¶mVal, error)) != NS_LDAP_SUCCESS) { if (*servers) __s_api_free2dArray(*servers); *servers = NULL; return (rc); } if (paramVal != NULL) { char **prefServers; void **val = NULL; if ((rc = __ns_ldap_getParam(NS_LDAP_PREF_ONLY_P, &val, error)) != NS_LDAP_SUCCESS) { if (*servers) __s_api_free2dArray(*servers); *servers = NULL; (void) __ns_ldap_freeParam(¶mVal); return (rc); } prefServers = (char **)paramVal; paramVal = NULL; if (prefServers) { if (val != NULL && (*val) != NULL && *(int *)val[0] == 1) sortServers = sortServerPref(*servers, prefServers, B_FALSE, version, &err); else sortServers = sortServerPref(*servers, prefServers, B_TRUE, version, &err); if (sortServers) { if (*servers) free(*servers); *servers = NULL; free(prefServers); prefServers = NULL; *servers = sortServers; } else { if (*servers) __s_api_free2dArray(*servers); *servers = NULL; __s_api_free2dArray(prefServers); prefServers = NULL; } } (void) __ns_ldap_freeParam(&val); } (void) __ns_ldap_freeParam(¶mVal); if (*servers == NULL) { if (err == NS_LDAP_CONFIG) { str = NULL_OR_STR(__s_api_get_configname( NS_LDAP_SERVERS_P)); str1 = NULL_OR_STR(__s_api_get_configname( NS_LDAP_SERVER_PREF_P)); (void) snprintf(errmsg, sizeof (errmsg), gettext("Unable to generate a new server list " "based on '%s' and/or '%s'"), str, str1); MKERROR(LOG_WARNING, *error, NS_CONFIG_FILE, strdup(errmsg), err); return (err); } return (NS_LDAP_MEMORY); } return (NS_LDAP_SUCCESS); } /* * FUNCTION: sortServerNet * Sort the serverlist based on the distance from client as long * as the list only contains IPv4 addresses. Otherwise do nothing. */ static char ** sortServerNet(char **srvlist) { int count = 0; int all = 0; int ipv4only = 1; struct ifinfo *ifs = __s_api_get_local_interfaces(); char **tsrvs; char **psrvs, **retsrvs; /* Sanity check. */ if (srvlist == NULL || srvlist[0] == NULL) return (NULL); /* Count the number of servers to sort. */ for (count = 0; srvlist[count] != NULL; count++) { if (!__s_api_isipv4(srvlist[count])) ipv4only = 0; } count++; /* Make room for the returned list of servers. */ retsrvs = (char **)calloc(count, sizeof (char *)); if (retsrvs == NULL) { free(ifs); ifs = NULL; return (NULL); } retsrvs[count - 1] = NULL; /* Make a temporary list of servers. */ psrvs = (char **)calloc(count, sizeof (char *)); if (psrvs == NULL) { free(ifs); ifs = NULL; free(retsrvs); retsrvs = NULL; return (NULL); } /* Filter servers on the same subnet */ tsrvs = srvlist; while (*tsrvs) { if (ipv4only && __s_api_IPv4sameNet(*tsrvs, ifs)) { psrvs[all] = *tsrvs; retsrvs[all++] = *(tsrvs); } tsrvs++; } /* Filter remaining servers. */ tsrvs = srvlist; while (*tsrvs) { char **ttsrvs = psrvs; while (*ttsrvs) { if (strcmp(*tsrvs, *ttsrvs) == 0) break; ttsrvs++; } if (*ttsrvs == NULL) retsrvs[all++] = *(tsrvs); tsrvs++; } free(ifs); ifs = NULL; free(psrvs); psrvs = NULL; return (retsrvs); } /* * FUNCTION: sortServerPref * Sort the serverlist based on the preferred server list. * * The sorting algorithm works as follows: * * If version 1, if flag is TRUE, find all the servers in both preflist * and srvlist, then append other servers in srvlist to this list * and return the list. * If flag is FALSE, just return srvlist. * srvlist can not be empty. * * If version 2, append all the servers in srvlist * but not in preflist to preflist, and return the merged list. * If srvlist is empty, just return preflist. * If preflist is empty, just return srvlist. */ static char ** sortServerPref(char **srvlist, char **preflist, boolean_t flag, int version, int *error) { int i, scount = 0, pcount = 0; int all = 0, dup = 0; char **tsrvs; char **retsrvs; char **dupsrvs; /* Count the number of servers to sort. */ if (srvlist && srvlist[0]) for (i = 0; srvlist[i] != NULL; i++) scount++; /* Sanity check. */ if (scount == 0 && version == 1) { *error = NS_LDAP_CONFIG; return (NULL); } /* Count the number of preferred servers */ if (preflist && preflist[0]) for (i = 0; preflist[i] != NULL; i++) pcount++; /* Sanity check. */ if (scount == 0 && pcount == 0) { *error = NS_LDAP_CONFIG; return (NULL); } /* Make room for the returned list of servers */ retsrvs = (char **)calloc(scount + pcount + 1, sizeof (char *)); if (retsrvs == NULL) { *error = NS_LDAP_MEMORY; return (NULL); } /* * if the preferred server list is empty, * just return a copy of the server list */ if (pcount == 0) { tsrvs = srvlist; while (*tsrvs) retsrvs[all++] = *(tsrvs++); return (retsrvs); } all = 0; /* * if the server list is empty, * just return a copy of the preferred server list */ if (scount == 0) { tsrvs = preflist; while (*tsrvs) retsrvs[all++] = *(tsrvs++); return (retsrvs); } all = 0; /* Make room for the servers whose memory needs to be freed */ dupsrvs = (char **)calloc(scount + pcount + 1, sizeof (char *)); if (dupsrvs == NULL) { free(retsrvs); *error = NS_LDAP_MEMORY; return (NULL); } /* * If version 1, * throw out preferred servers not on server list. * If version 2, make a copy of the preferred server list. */ if (version == 1) { tsrvs = preflist; while (*tsrvs) { char **ttsrvs = srvlist; while (*ttsrvs) { if (strcmp(*tsrvs, *(ttsrvs)) == 0) break; ttsrvs++; } if (*ttsrvs != NULL) retsrvs[all++] = *tsrvs; else dupsrvs[dup++] = *tsrvs; tsrvs++; } } else { tsrvs = preflist; while (*tsrvs) retsrvs[all++] = *(tsrvs++); } /* * If version 1, * if PREF_ONLY is false, we append the non-preferred servers * to bottom of list. * For version 2, always append. */ if (flag == B_TRUE || version != 1) { tsrvs = srvlist; while (*tsrvs) { char **ttsrvs = preflist; while (*ttsrvs) { if (strcmp(*tsrvs, *ttsrvs) == 0) { break; } ttsrvs++; } if (*ttsrvs == NULL) retsrvs[all++] = *tsrvs; else dupsrvs[dup++] = *tsrvs; tsrvs++; } } /* free memory for duplicate servers */ if (dup) { for (tsrvs = dupsrvs; *tsrvs; tsrvs++) free(*tsrvs); } free(dupsrvs); return (retsrvs); } /* * FUNCTION: __s_api_removeBadServers * Contacts the ldap cache manager for marking the * problem servers as down, so that the server is * not contacted until the TTL expires. */ void __s_api_removeBadServers(char ** Servers) { char **host; if (Servers == NULL) return; for (host = Servers; *host != NULL; host++) { if (__s_api_removeServer(*host) < 0) { /* * Couldn't remove server from * server list. Log a warning. */ syslog(LOG_WARNING, "libsldap: could " "not remove %s from servers list", *host); } } } /* * FUNCTION: __s_api_free2dArray */ void __s_api_free2dArray(char ** inarray) { char **temptr; if (inarray == NULL) return; for (temptr = inarray; *temptr != NULL; temptr++) { free(*temptr); } free(inarray); } /* * FUNCTION: __s_api_cp2dArray */ char ** __s_api_cp2dArray(char **inarray) { char **newarray; char **ttarray, *ret; int count; if (inarray == NULL) return (NULL); for (count = 0; inarray[count] != NULL; count++) ; newarray = (char **)calloc(count + 1, sizeof (char *)); if (newarray == NULL) return (NULL); ttarray = newarray; for (; *inarray; inarray++) { *(ttarray++) = ret = strdup(*inarray); if (ret == NULL) { __s_api_free2dArray(newarray); return (NULL); } } return (newarray); } /* * FUNCTION: __s_api_isCtrlSupported * Determines if the passed control is supported by the LDAP sever. * RETURNS: NS_LDAP_SUCCESS if yes, NS_LDAP_OP_FAIL if not. */ int __s_api_isCtrlSupported(Connection *con, char *ctrlString) { char **ctrl; int len; len = strlen(ctrlString); for (ctrl = con->controls; ctrl && *ctrl; ctrl++) { if (strncasecmp(*ctrl, ctrlString, len) == 0) return (NS_LDAP_SUCCESS); } return (NS_LDAP_OP_FAILED); } /* * FUNCTION: __s_api_toFollowReferrals * Determines if need to follow referral for an SLDAP API. * RETURN VALUES: NS_LDAP_SUCCESS, NS_LDAP_INVALID_PARAM, or * other rc from __ns_ldap_getParam() * INPUT: flags * OUTPUT: toFollow, errorp */ int __s_api_toFollowReferrals(const int flags, int *toFollow, ns_ldap_error_t **errorp) { void **paramVal = NULL; int rc = 0; int iflags = 0; #ifdef DEBUG (void) fprintf(stderr, "__s_api_toFollowReferrals START\n"); #endif /* Either NS_LDAP_NOREF or NS_LDAP_FOLLOWREF not both */ if ((flags & (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) == (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) { return (NS_LDAP_INVALID_PARAM); } /* * if the NS_LDAP_NOREF or NS_LDAP_FOLLOWREF is set * this will take precendence over the values specified * in the configuration file */ if (flags & (NS_LDAP_NOREF | NS_LDAP_FOLLOWREF)) { iflags = flags; } else { rc = __ns_ldap_getParam(NS_LDAP_SEARCH_REF_P, ¶mVal, errorp); if (rc != NS_LDAP_SUCCESS) return (rc); if (paramVal == NULL || *paramVal == NULL) { (void) __ns_ldap_freeParam(¶mVal); if (*errorp) (void) __ns_ldap_freeError(errorp); *toFollow = TRUE; return (NS_LDAP_SUCCESS); } iflags = (* (int *)(*paramVal)); (void) __ns_ldap_freeParam(¶mVal); } if (iflags & NS_LDAP_NOREF) *toFollow = FALSE; else *toFollow = TRUE; return (NS_LDAP_SUCCESS); } /* * FUNCTION: __s_api_addRefInfo * Insert a referral info into a referral info list. * RETURN VALUES: NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_OP_FAILED * INPUT: LDAP URL, pointer to the referral info list, * search baseDN, search scope, search filter, * previous connection */ int __s_api_addRefInfo(ns_referral_info_t **head, char *url, char *baseDN, int *scope, char *filter, LDAP *ld) { char errmsg[MAXERROR], *tmp; ns_referral_info_t *ref, *tmpref; LDAPURLDesc *ludp = NULL; int hostlen; char *ld_defhost = NULL; #ifdef DEBUG (void) fprintf(stderr, "__s_api_addRefInfo START\n"); #endif /* sanity check */ if (head == NULL) return (NS_LDAP_OP_FAILED); /* * log error and return NS_LDAP_SUCCESS * if one of the following: * 1. non-LDAP URL * 2. LDAP URL which can not be parsed */ if (!ldap_is_ldap_url(url) || ldap_url_parse_nodn(url, &ludp) != 0) { (void) snprintf(errmsg, MAXERROR, "%s: %s", gettext("Invalid or non-LDAP URL when" " processing referrals URL"), url); syslog(LOG_ERR, "libsldap: %s", errmsg); if (ludp) ldap_free_urldesc(ludp); return (NS_LDAP_SUCCESS); } ref = (ns_referral_info_t *)calloc(1, sizeof (ns_referral_info_t)); if (ref == NULL) { ldap_free_urldesc(ludp); return (NS_LDAP_MEMORY); } /* * we do have a valid URL and we were able to parse it * however, we still need to find out what hostport to * use if none were provided in the LDAP URL * (e.g., ldap:///...) */ if ((ludp->lud_port == 0) && (ludp->lud_host == NULL)) { if (ld == NULL) { (void) snprintf(errmsg, MAXERROR, "%s: %s", gettext("no LDAP handle when" " processing referrals URL"), url); syslog(LOG_WARNING, "libsldap: %s", errmsg); ldap_free_urldesc(ludp); free(ref); return (NS_LDAP_SUCCESS); } else { (void) ldap_get_option(ld, LDAP_OPT_HOST_NAME, &ld_defhost); if (ld_defhost == NULL) { (void) snprintf(errmsg, MAXERROR, "%s: %s", gettext("not able to retrieve default " "host when processing " "referrals URL"), url); syslog(LOG_WARNING, "libsldap: %s", errmsg); ldap_free_urldesc(ludp); free(ref); return (NS_LDAP_SUCCESS); } else { ref->refHost = strdup(ld_defhost); if (ref->refHost == NULL) { ldap_free_urldesc(ludp); free(ref); return (NS_LDAP_MEMORY); } } } } else { /* * add 4 here: * 1 for the last '\0'. * 1 for host and prot separator ":" * and "[" & "]" for possible ipV6 addressing */ hostlen = strlen(ludp->lud_host) + sizeof (MAXPORTNUMBER_STR) + 4; ref->refHost = (char *)malloc(hostlen); if (ref->refHost == NULL) { ldap_free_urldesc(ludp); free(ref); return (NS_LDAP_MEMORY); } if (ludp->lud_port != 0) { /* * serverAddr = host:port * or * if host is an IPV6 address * [host]:port */ tmp = strstr(url, ludp->lud_host); if (tmp && (tmp > url) && *(tmp - 1) == '[') { (void) snprintf(ref->refHost, hostlen, "[%s]:%d", ludp->lud_host, ludp->lud_port); } else { (void) snprintf(ref->refHost, hostlen, "%s:%d", ludp->lud_host, ludp->lud_port); } } else { /* serverAddr = host */ (void) snprintf(ref->refHost, hostlen, "%s", ludp->lud_host); } } if (ludp->lud_dn) { ref->refDN = strdup(ludp->lud_dn); if (ref->refDN == NULL) { ldap_free_urldesc(ludp); free(ref->refHost); free(ref); return (NS_LDAP_MEMORY); } } else { if (baseDN) { ref->refDN = strdup(baseDN); if (ref->refDN == NULL) { ldap_free_urldesc(ludp); free(ref->refHost); free(ref); return (NS_LDAP_MEMORY); } } } if (filter) ref->refFilter = strdup(filter); else if (ludp->lud_filter) ref->refFilter = strdup(ludp->lud_filter); else ref->refFilter = strdup(""); if (ref->refFilter == NULL) { ldap_free_urldesc(ludp); free(ref->refHost); if (ref->refDN) free(ref->refDN); free(ref); return (NS_LDAP_MEMORY); } /* * If the scope is specified in the URL use it. * Note if the scope is missing in the URL, ldap_url_parse_nodn() * returns the scope BASE. We need to check that the scope of BASE * is actually present in the URL. * If the scope is missing in the URL then use the passed-in * scope. * If there is no passed-in scope, then use the scope SUBTREE. */ if (ludp->lud_dn && ludp->lud_scope != LDAP_SCOPE_BASE) ref->refScope = ludp->lud_scope; else if (ludp->lud_dn && strstr(url, "?base")) ref->refScope = LDAP_SCOPE_BASE; else if (scope) ref->refScope = *scope; else ref->refScope = LDAP_SCOPE_SUBTREE; ref->next = NULL; ldap_free_urldesc(ludp); /* insert the referral info */ if (*head) { for (tmpref = *head; tmpref->next; tmpref = tmpref->next) ; tmpref->next = ref; } else *head = ref; return (NS_LDAP_SUCCESS); } /* * FUNCTION: __s_api_deleteRefInfo * Delete a referral info list. * INPUT: pointer to the referral info list */ void __s_api_deleteRefInfo(ns_referral_info_t *head) { ns_referral_info_t *ref, *tmp; #ifdef DEBUG (void) fprintf(stderr, "__s_api_deleteRefInfo START\n"); #endif for (ref = head; ref; ) { if (ref->refHost) free(ref->refHost); if (ref->refDN) free(ref->refDN); if (ref->refFilter) free(ref->refFilter); tmp = ref->next; free(ref); ref = tmp; } } /* * FUNCTION: __s_api_get_SSD_from_SSDtoUse_service * * Retrieves the Service Search Descriptors which should be used for * the given service. For example, return all the "passwd" SSDs for * service "shadow" if no SSD is defined for service "shadow" and * no filter component is defined in all the "passwd" SSDs. This idea * of sharing the SSDs defined for some other service is to reduce the * configuration complexity. For a service, which does not have its own * entries in the LDAP directory, SSD for it is useless, and should not * be set. But since this service must share the container with at least * one other service which does have it own entries, the SSD for * this other service will be shared by this service. * This other service is called the SSD-to-use service. * The static data structure, ns_def_map[], in this file * defines the SSD-to-use service for all the services supported. * * RETURN VALUES: NS_LDAP_SUCCESS, NS_LDAP_MEMORY, NS_LDAP_INVALID_PARAM * INPUT: service * OUTPUT: *SSDlist, *errorp if error */ int __s_api_get_SSD_from_SSDtoUse_service(const char *service, ns_ldap_search_desc_t ***SSDlist, ns_ldap_error_t **errorp) { int i, rc; int found = FALSE; int filter_found = FALSE; char *SSD_service = NULL; char errmsg[MAXERROR]; ns_ldap_search_desc_t **sdlist; int auto_service = FALSE; #ifdef DEBUG (void) fprintf(stderr, "__s_api_get_SSD_from_SSDtoUse_service START\n"); #endif if (SSDlist == NULL || errorp == NULL) return (NS_LDAP_INVALID_PARAM); *SSDlist = NULL; *errorp = NULL; if (service == NULL) return (NS_LDAP_SUCCESS); if (strncasecmp(service, "auto_", 5) == 0) auto_service = TRUE; /* * First try to return the configured SSDs for the input server */ rc = __ns_ldap_getSearchDescriptors(service, SSDlist, errorp); if (rc != NS_LDAP_SUCCESS) return (rc); else { if (*SSDlist != NULL) return (NS_LDAP_SUCCESS); } /* * If service == auto_* and SSD is not found, * then try automount to see if there is an SSD * for automount. */ if (auto_service) { rc = __ns_ldap_getSearchDescriptors( "automount", SSDlist, errorp); if (rc != NS_LDAP_SUCCESS) return (rc); else { if (*SSDlist != NULL) { /* * If SSDlist is found, * prepend automountMapName to the basedn * in the SSDlist * */ rc = __s_api_prepend_automountmapname( service, SSDlist, errorp); if (rc != NS_LDAP_SUCCESS) { (void) __ns_ldap_freeSearchDescriptors( SSDlist); *SSDlist = NULL; } return (rc); } } } /* * Find the SSDtoUse service. * If none found, flag "found" remains FALSE. */ for (i = 0; ns_def_map[i].service != NULL; i++) { if (ns_def_map[i].SSDtoUse_service && strcasecmp(service, ns_def_map[i].service) == 0) { found = TRUE; SSD_service = ns_def_map[i].SSDtoUse_service; break; } } if (!found) return (NS_LDAP_SUCCESS); /* * return the SSDs for SSD_service only if no optional filter * component is defined in the SSDs */ rc = __ns_ldap_getSearchDescriptors(SSD_service, SSDlist, errorp); if (rc != NS_LDAP_SUCCESS) { return (rc); } else { if (*SSDlist == NULL) return (NS_LDAP_SUCCESS); /* check to see if filter defined in SSD */ for (sdlist = *SSDlist; *sdlist; sdlist++) { if ((*sdlist)->filter && strlen((*sdlist)->filter) > 0) { filter_found = TRUE; break; } } if (filter_found) { (void) __ns_ldap_freeSearchDescriptors(SSDlist); *SSDlist = NULL; (void) snprintf(errmsg, sizeof (errmsg), gettext("Service search descriptor for " "service '%s' contains filter, " "which can not be used for " "service '%s'."), SSD_service, service); MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg), NS_LDAP_CONFIG); return (NS_LDAP_CONFIG); } } return (NS_LDAP_SUCCESS); } /* * verify addr is an IPv4 address with the optional [:portno] * RFC2373 & RFC2732 & RFC2396 */ int __s_api_isipv4(char *addr) { int i, seg, digit, port; if (!addr) return (0); digit = seg = port = 0; for (i = 0; i < strlen(addr); i++) { if (isdigit(addr[i])) { digit++; continue; } if (addr[i] == '.') { if (digit > 3 || digit == 0) return (0); digit = 0; seg++; continue; } if (addr[i] == ':') { if (digit > 3) return (0); port++; digit = 0; seg++; continue; } return (0); } if ((seg == 3 && port == 0 && digit > 0 && digit < 4) || (seg == 4 && port == 1 && digit > 0)) return (1); return (0); } /* * verify addr is an IPv6 address with the optional [IPv6]:portno * RFC2373 & RFC2732 & RFC2396 */ int __s_api_isipv6(char *addr) { int i, col, digit, port, dc, tc; char *laddr, *c1, *s; if (!addr) return (0); s = addr; laddr = NULL; digit = col = port = 0; if (addr[0] == '[') { laddr = strdup(addr); if (!laddr) return (0); c1 = strchr(laddr, ']'); /* only 1 ']' should be in an addr */ if (!c1 || (strchr(c1+1, ']'))) goto bad; switch (c1[1]) { case ':': port++; for (i = 2; i < strlen(c1); i++) { if (!isdigit(c1[i])) goto bad; digit++; } if (!digit) goto bad; c1[0] = '\0'; break; case '\0': c1[0] = '\0'; break; default: goto bad; } s = &laddr[1]; } digit = dc = tc = 0; for (i = 0; i < strlen(s); i++) { if (isxdigit(s[i])) { if (digit == 0) dc = i; digit++; col = 0; continue; } if (s[i] == ':') { tc++; if ((col > 1) || (i && !col && !digit)) goto bad; digit = 0; col++; continue; } if (s[i] == '.') { if (__s_api_isipv4(&s[dc]) && tc) goto good; else goto bad; } goto bad; } good: free(laddr); return (1); bad: free(laddr); return (0); } /* * verify addr is a valid hostname with the optional [:portno] * RFC2373 & RFC2732 & RFC2396 */ int __s_api_ishost(char *addr) { int i, seg, alpha, digit, port; if (!addr) return (0); alpha = digit = seg = port = 0; /* must start with alpha character */ if (!isalpha(addr[0])) return (0); for (i = 0; i < strlen(addr); i++) { if (isalpha(addr[i]) || (i && addr[i] == '-')) { alpha++; continue; } if (isdigit(addr[i])) { digit++; continue; } if (addr[i] == '.') { if (!alpha && !digit) return (0); alpha = digit = 0; seg++; continue; } if (addr[i] == ':') { if (!alpha && !digit) return (0); alpha = digit = 0; port++; seg++; continue; } return (0); } if ((port == 0 && (seg || alpha || digit)) || (port == 1 && alpha == 0 && digit)) return (1); return (0); } /* * Prepend automountMapName=auto_xxx to the basedn * in the SSDlist */ int __s_api_prepend_automountmapname( const char *service, ns_ldap_search_desc_t ***SSDlist, ns_ldap_error_t **errorp) { int i, rc; ns_ldap_search_desc_t ** ssdlist = NULL; if (service == NULL || SSDlist == NULL || *SSDlist == NULL) return (NS_LDAP_INVALID_PARAM); ssdlist = *SSDlist; for (i = 0; ssdlist[i] != NULL; i++) { rc = __s_api_prepend_automountmapname_to_dn( service, &ssdlist[i]->basedn, errorp); if (rc != NS_LDAP_SUCCESS) return (rc); } return (NS_LDAP_SUCCESS); } /* * Prepend automountMapName=auto_xxx to the DN * Construct a string of * "automountMapName=auto_xxx,dn" * * If automountMapName is mapped to some other attribute, * then use the mapping in the setup. * * If a version 1 profile is in use, use nisMapName for * backward compatibility (i.e. "nisMapName=auto_xxx,dn"). */ int __s_api_prepend_automountmapname_to_dn( const char *service, char **dn, ns_ldap_error_t **errorp) { int rc, len_s = 0, len_d = 0, len = 0; char *buffer = NULL; char *default_automountmapname = "automountMapName"; char *automountmapname = NULL; char **mappedattrs = NULL; char errstr[MAXERROR]; void **paramVal = NULL; if (service == NULL || dn == NULL || *dn == NULL) return (NS_LDAP_INVALID_PARAM); rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P, ¶mVal, errorp); if (rc != NS_LDAP_SUCCESS || !paramVal || !*paramVal) { if (paramVal) (void) __ns_ldap_freeParam(¶mVal); return (rc); } if (strcasecmp(*paramVal, NS_LDAP_VERSION_1) == 0) { automountmapname = strdup("nisMapName"); (void) __ns_ldap_freeParam(¶mVal); if (automountmapname == NULL) { return (NS_LDAP_MEMORY); } } else { (void) __ns_ldap_freeParam(¶mVal); /* Find mapped attribute name of auto_xxx first */ mappedattrs = __ns_ldap_getMappedAttributes( service, default_automountmapname); /* * if mapped attribute name of auto_xxx is not found, * find the mapped attribute name of automount */ if (mappedattrs == NULL) mappedattrs = __ns_ldap_getMappedAttributes( "automount", default_automountmapname); /* * if mapped attr is not found, use the default automountmapname */ if (mappedattrs == NULL) { automountmapname = strdup(default_automountmapname); if (automountmapname == NULL) return (NS_LDAP_MEMORY); } else { if (mappedattrs[0] != NULL) { /* * Copy it from the mapped attr list * Assume it's 1 to 1 mapping * 1 to n does not make sense */ automountmapname = strdup(mappedattrs[0]); __s_api_free2dArray(mappedattrs); if (automountmapname == NULL) { return (NS_LDAP_MEMORY); } } else { /* * automountmapname is mapped to an empty string */ __s_api_free2dArray(mappedattrs); (void) sprintf(errstr, gettext( "Attribute automountMapName is " "mapped to an empty string.\n")); MKERROR(LOG_WARNING, *errorp, NS_CONFIG_SYNTAX, strdup(errstr), NS_LDAP_MEMORY); return (NS_LDAP_CONFIG); } } } len_s = strlen(service); len_d = strlen(*dn); /* automountMapName + "=" + service + "," + dn + '\0' */ len = strlen(automountmapname) + 1 + len_s + 1 + len_d + 1; buffer = (char *)malloc(len); if (buffer == NULL) { free(automountmapname); return (NS_LDAP_MEMORY); } (void) snprintf(buffer, len, "%s=%s,%s", automountmapname, service, *dn); buffer[len-1] = '\0'; free(automountmapname); /* free the original dn */ (void) free(*dn); *dn = buffer; return (NS_LDAP_SUCCESS); } /* * Map the LDAP error code and error message from LDAP server * to a password status used for password aging/management. */ ns_ldap_passwd_status_t __s_api_set_passwd_status(int errnum, char *errmsg) { syslog(LOG_DEBUG, "libsldap: got LDAP errnum %d & message: %s ", errnum, (errmsg != NULL) ? errmsg : "error msg not available"); if (errmsg) { if (errnum == LDAP_INVALID_CREDENTIALS) { /* * case 1 (Bind): * password expired */ if (strstr(errmsg, NS_PWDERR_EXPIRED)) return (NS_PASSWD_EXPIRED); } if (errnum == LDAP_UNWILLING_TO_PERFORM) { /* * case 1.1 (Bind): * password expired */ if (strstr(errmsg, NS_PWDERR_EXPIRED)) return (NS_PASSWD_EXPIRED); /* * case 2 (Bind): * Account inactivated */ if (strstr(errmsg, NS_PWDERR_ACCT_INACTIVATED)) return (NS_PASSWD_EXPIRED); /* * case 3 (Modify passwd): * the user is not allow to change * password; only admin can change it */ if (strstr(errmsg, NS_PWDERR_CHANGE_NOT_ALLOW)) return (NS_PASSWD_CHANGE_NOT_ALLOWED); } if (errnum == LDAP_CONSTRAINT_VIOLATION) { /* * case 4 (Bind): * the user account is locked due to * too many login failures. */ if (strstr(errmsg, NS_PWDERR_MAXTRIES)) return (NS_PASSWD_RETRY_EXCEEDED); /* * case 5 (Modify passwd): * syntax error: the new password * has length less than defined * minimum * Not true anymore with strong password * policies on LDAP server: errmsg that * contain NS_PWDERR_INVALID_SYNTAX may * have different meanings. * To keep compatibility with older password * policy, check if errmsg is strictly equal * to NS_PWDERR_INVALID_SYNTAX and if yes only, * return NS_PASSWD_TOO_SHORT. */ if (strcmp(errmsg, NS_PWDERR_INVALID_SYNTAX) == 0) return (NS_PASSWD_TOO_SHORT); if (strstr(errmsg, NS_PWDERR_INVALID_SYNTAX)) return (NS_PASSWD_INVALID_SYNTAX); /* * case 6 (Modify passwd): * trivial password: same value as * that of attribute cn, sn, or uid ... */ if (strstr(errmsg, NS_PWDERR_TRIVIAL_PASSWD)) return (NS_PASSWD_INVALID_SYNTAX); /* * case 7 (Modify passwd): * re-use one of the old passwords * in history list */ if (strstr(errmsg, NS_PWDERR_IN_HISTORY)) return (NS_PASSWD_IN_HISTORY); /* * case 8 (Modify passwd): * password not allowed to be * changed yet; within minimum * age */ if (strstr(errmsg, NS_PWDERR_WITHIN_MIN_AGE)) return (NS_PASSWD_WITHIN_MIN_AGE); } } return (NS_PASSWD_GOOD); } /* * Determine if the input OID list contains * one of the password control OIDs, which are: * LDAP_CONTROL_PWEXPIRED: 2.16.840.1.113730.3.4.4 * LDAP_CONTROL_PWEXPIRING: 2.16.840.1.113730.3.4.5. * If yes, return 1, if no, 0. */ int __s_api_contain_passwd_control_oid(char **oids) { char **oid; if (oids == NULL) return (0); for (oid = oids; *oid; oid++) { if (strcmp(*oid, LDAP_CONTROL_PWEXPIRED) == 0 || strcmp(*oid, LDAP_CONTROL_PWEXPIRING) == 0) { return (1); } } return (0); } /* * Determine if the input OID list contains LDAP V3 password less * account management control OID, which is: * NS_LDAP_ACCOUNT_USABLE_CONTROL:1.3.6.1.4.1.42.2.27.9.5.8 * If yes, return 1, if no, 0. */ int __s_api_contain_account_usable_control_oid(char **oids) { char **oid; if (oids == NULL) return (0); for (oid = oids; *oid; oid++) { if (strcmp(*oid, NS_LDAP_ACCOUNT_USABLE_CONTROL) == 0) { return (1); } } return (0); } /* * For some databases in name switch, the name and aliases are saved * as "cn". When the "cn" valuse are retrieved, there is no distinction * which is the name and which is(are) aliase(s). * This function is to parse RDN and find the value of the "cn" and * then find the matching value in "cn" attribute. * Also see RFC 2307 section 5.6. * * Input - * entry: An LDAP entry * attrptr: A attribute which value appears in RDN * This should be "cn" for the name switch for now. * case_ignore: 0 Case sensitive comparison on the attribute value * 1 Case insensitive comparison * * Return - * The value of an attrbute which is used as canonical name * This is read only and the caller should not try to free it. * If it's a NULL, it could be either an RDN parsing error * or RDN value does not match any existing "cn" values. * e.g. * dn: cn=xx+ipserviceprotocol=udp,...... * cn: aa * cn: bb * * Note: * Although the name switch/ldap's rdn is in "cn=xx" or "cn=xx+..." * format, this function makes no such assumption. If the DN * is saved as "dn: yy=...+sn=my_canocical_name, ..", then it can still work. * The comments use "cn" as an example only. * */ typedef int (*cmpfunc)(const char *, const char *); char * __s_api_get_canonical_name(ns_ldap_entry_t *entry, ns_ldap_attr_t *attrptr, int case_ignore) { uint_t i; char *token, *lasts, *value = NULL; char **rdn = NULL, **attrs = NULL, **values = NULL; char *rdn_attr_value = NULL; cmpfunc cmp; if (entry == NULL || attrptr == NULL) return (NULL); /* "values" is read-only */ if ((values = __ns_ldap_getAttr(entry, "dn")) == NULL || values[0] == NULL) return (NULL); if ((rdn = ldap_explode_dn(values[0], 0)) == NULL || rdn[0] == NULL) return (NULL); if ((attrs = ldap_explode_rdn(rdn[0], 0)) == NULL) { ldap_value_free(rdn); return (NULL); } /* Assume the rdn is normalized */ for (i = 0; attrs[i] != NULL; i++) { /* parse attribute name and value, get attribute name first */ if ((token = strtok_r(attrs[i], "=", &lasts)) == NULL) { goto cleanup; } if (strcasecmp(token, attrptr->attrname) == 0) { /* get value */ rdn_attr_value = lasts; break; } } if (rdn_attr_value) { if (case_ignore) cmp = strcasecmp; else cmp = strcmp; /* * After parsing RDN and find the matching attribute in RDN, * match rdn value with values in "cn". */ for (i = 0; i < attrptr->value_count; i++) { if (attrptr->attrvalue[i] && (*cmp)(rdn_attr_value, attrptr->attrvalue[i]) == 0) { /* RDN "cn" value matches the "cn" value */ value = attrptr->attrvalue[i]; break; } } } cleanup: ldap_value_free(rdn); ldap_value_free(attrs); return (value); } /* * This function requests a server to be removed from * the cache manager maintained server list. This is * done via the door functionality. * Returns 0 if OK, else a negative value. */ int __s_api_removeServer(const char *server) { union { ldap_data_t s_d; char s_b[DOORBUFFERSIZE]; } space; ns_server_info_t r, *ret = &r; const char *ireq; ldap_data_t *sptr; int ndata; int adata; int len; int rc; ns_ldap_error_t *error = NULL; if (server == NULL) return (-1); ireq = NS_CACHE_NORESP; if (__s_api_isStandalone()) { /* * Remove 'server' from the standalone server list. * __s_api_findRootDSE() is the standalone version * of getldap_get_serverInfo() used in ldap_cachemgr. * Request NS_CACHE_NORESP indicates 'server' should * be removed. */ if (__s_api_findRootDSE(ireq, server, NS_CACHE_ADDR_IP, NULL, &error) != NS_LDAP_SUCCESS) { syslog(LOG_WARNING, "libsldap (\"standalone\" mode): " " Unable to remove %s - %s", server, error != NULL && error->message != NULL ? error->message : " no error info"); if (error != NULL) { (void) __ns_ldap_freeError(&error); } return (NS_CACHE_NOSERVER); } return (0); } (void) memset(ret, 0, sizeof (ns_server_info_t)); (void) memset(space.s_b, 0, DOORBUFFERSIZE); adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(NS_CACHE_ADDR_IP) + 1); adata += strlen(DOORLINESEP) + 1; adata += strlen(server) + 1; ndata = sizeof (space); space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER; len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber); if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len) return (-1); if (strlcat(space.s_d.ldap_call.ldap_u.domainname, NS_CACHE_ADDR_IP, len) >= len) return (-1); if (strlcat(space.s_d.ldap_call.ldap_u.domainname, DOORLINESEP, len) >= len) return (-1); if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server, len) >= len) return (-1); sptr = &space.s_d; /* try to remove the server via the door interface */ rc = __ns_ldap_trydoorcall(&sptr, &ndata, &adata); /* clean up the door call */ if (sptr != &space.s_d) { (void) munmap((char *)sptr, ndata); } return (rc); } void __s_api_free_server_info(ns_server_info_t *sinfo) { if (sinfo->server) { free(sinfo->server); sinfo->server = NULL; } if (sinfo->serverFQDN) { free(sinfo->serverFQDN); sinfo->serverFQDN = NULL; } __s_api_free2dArray(sinfo->saslMechanisms); sinfo->saslMechanisms = NULL; __s_api_free2dArray(sinfo->controls); sinfo->controls = NULL; } /* * Create an ns_ldap_error structure, set status to 'rc', * and copy in the error message 'msg'. */ ns_ldap_error_t * __s_api_make_error(int rc, char *msg) { ns_ldap_error_t *ep; ep = (ns_ldap_error_t *)calloc(1, sizeof (*ep)); if (ep == NULL) return (NULL); ep->status = rc; if (msg != NULL) ep->message = strdup(msg); /* OK if ep->message is NULL */ return (ep); } /* * Make a copy of the input ns_ldap_error. */ ns_ldap_error_t * __s_api_copy_error(ns_ldap_error_t *errorp) { ns_ldap_error_t *ep; char *msg; if (errorp == NULL) return (NULL); ep = (ns_ldap_error_t *)malloc(sizeof (*ep)); if (ep != NULL) { *ep = *errorp; if (ep->message != NULL) { msg = strdup(ep->message); if (msg == NULL) { free(ep); ep = NULL; } else ep->message = msg; } } return (ep); }