/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* libsldap - cachemgr side configuration components */ #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 ALWAYS 1 /* * ************************************************************** * Configuration File Routines * ************************************************************** */ /* Size of the errstr buffer needs to be MAXERROR */ static int read_line(FILE *fp, char *buffer, int buflen, char *errstr) { int linelen; char c; *errstr = '\0'; for (linelen = 0; linelen < buflen; ) { c = getc(fp); if (c == EOF) break; switch (c) { case '\n': if (linelen > 0 && buffer[linelen - 1] == '\\') { /* Continuation line found */ --linelen; } else { /* end of line found */ buffer[linelen] = '\0'; return (linelen); } break; default: buffer[linelen++] = c; } } if (linelen >= buflen) { (void) snprintf(errstr, MAXERROR, gettext("Buffer overflow, line too long.")); return (-2); } else if (linelen > 0 && buffer[linelen - 1] == '\\') { (void) snprintf(errstr, MAXERROR, gettext("Unterminated continuation line.")); return (-2); } else { /* end of file */ buffer[linelen] = '\0'; } return (linelen > 0 ? linelen : -1); } static ns_parse_status read_file(ns_config_t *ptr, int cred_file, ns_ldap_error_t **error) { ParamIndexType i = 0; char errstr[MAXERROR]; char buffer[BUFSIZE], *name, *value; int emptyfile, lineno; FILE *fp; int ret; int linelen; char *file; int first = 1; if (cred_file) { file = NSCREDFILE; } else { file = NSCONFIGFILE; } fp = fopen(file, "rF"); if (fp == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("Unable to open filename '%s' " "for reading (errno=%d)."), file, errno); MKERROR(LOG_ERR, *error, NS_CONFIG_FILE, strdup(errstr), NULL); return (NS_NOTFOUND); } emptyfile = 1; lineno = 0; for (; ; ) { if ((linelen = read_line(fp, buffer, sizeof (buffer), errstr)) < 0) /* End of file */ break; lineno++; if (linelen == 0) continue; /* get rid of comment lines */ if (buffer[0] == '#') continue; emptyfile = 0; name = NULL; value = NULL; __s_api_split_key_value(buffer, &name, &value); if (name == NULL || value == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("Missing Name or Value on line %d."), lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); (void) fclose(fp); return (NS_PARSE_ERR); } if (__s_api_get_versiontype(ptr, name, &i) != 0) { (void) snprintf(errstr, sizeof (errstr), gettext("Illegal profile type on line %d."), lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); (void) fclose(fp); return (NS_PARSE_ERR); } if (!first && i == NS_LDAP_FILE_VERSION_P) { (void) snprintf(errstr, sizeof (errstr), gettext("Illegal NS_LDAP_FILE_VERSION " "on line %d."), lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); (void) fclose(fp); return (NS_PARSE_ERR); } first = 0; switch (__s_api_get_configtype(i)) { case SERVERCONFIG: case CLIENTCONFIG: if (cred_file == 0) { ret = __ns_ldap_setParamValue(ptr, i, value, error); if (ret != NS_SUCCESS) { (void) fclose(fp); return (ret); } } else if (i != NS_LDAP_FILE_VERSION_P) { (void) snprintf(errstr, sizeof (errstr), gettext("Illegal entry in '%s' on " "line %d"), file, lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); (void) fclose(fp); return (NS_PARSE_ERR); } break; case CREDCONFIG: if (i == NS_LDAP_FILE_VERSION_P) break; if (cred_file) { ret = __ns_ldap_setParamValue(ptr, i, value, error); if (ret != NS_SUCCESS) { (void) fclose(fp); return (ret); } } else { (void) snprintf(errstr, sizeof (errstr), gettext("Illegal entry in '%s' on " "line %d"), file, lineno); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); (void) fclose(fp); return (NS_PARSE_ERR); } } } (void) fclose(fp); if (!cred_file && emptyfile) { /* Error in read_line */ (void) snprintf(errstr, sizeof (errstr), gettext("Empty config file: '%s'"), file); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); return (NS_PARSE_ERR); } if (linelen == -2) { /* Error in read_line */ (void) snprintf(errstr, sizeof (errstr), gettext("Line too long in '%s'"), file); MKERROR(LOG_ERR, *error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); return (NS_PARSE_ERR); } return (NS_SUCCESS); } /* * Cache Manager side of configuration file loading */ ns_ldap_error_t * __ns_ldap_LoadConfiguration() { ns_ldap_error_t *error = NULL; ns_config_t *ptr = NULL; char errstr[MAXERROR]; ns_parse_status ret; ptr = __s_api_create_config(); if (ptr == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("__ns_ldap_LoadConfiguration: Out of memory.")); MKERROR(LOG_ERR, error, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (error); } /* Load in Configuration file */ ret = read_file(ptr, 0, &error); if (ret != NS_SUCCESS) { __s_api_destroy_config(ptr); return (error); } /* Load in Credential file */ ret = read_file(ptr, 1, &error); if (ret != NS_SUCCESS) { __s_api_destroy_config(ptr); return (error); } if (__s_api_crosscheck(ptr, errstr, B_TRUE) != NS_SUCCESS) { __s_api_destroy_config(ptr); MKERROR(LOG_ERR, error, NS_CONFIG_SYNTAX, strdup(errstr), NULL); return (error); } __s_api_init_config(ptr); return (NULL); } static int _print2buf(LineBuf *line, char *toprint, int addsep) { int newsz = 0; int newmax = 0; char *str; if (line == NULL) return (-1); newsz = strlen(toprint) + line->len + 1; if (addsep) { newsz += strlen(DOORLINESEP); } if (line->alloc == 0 || newsz > line->alloc) { /* Round up to next buffer and add 1 */ newmax = (((newsz+(BUFSIZ-1))/BUFSIZ)+1) * BUFSIZ; if (line->alloc == 0) line->str = (char *)calloc(newmax, 1); else { /* * if realloc() returns NULL, * the original buffer is untouched. * It needs to be freed. */ str = (char *)realloc(line->str, newmax); if (str == NULL) { free(line->str); line->str = NULL; } else line->str = str; } line->alloc = newmax; if (line->str == NULL) { line->alloc = 0; line->len = 0; return (-1); } } /* now add new 'toprint' data to buffer */ (void) strlcat(line->str, toprint, line->alloc); if (addsep) { (void) strlcat(line->str, DOORLINESEP, line->alloc); } line->len = newsz; return (0); } /* * __ns_ldap_LoadDoorInfo is a routine used by the ldapcachemgr * to create a configuration buffer to transmit back to a client * domainname is transmitted to ldapcachemgr and ldapcachemgr uses * it to select a configuration to transmit back. Otherwise it * is essentially unused in sldap. */ ns_ldap_error_t * __ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname) { ns_config_t *ptr; char errstr[MAXERROR]; ns_ldap_error_t *errorp; char string[BUFSIZE]; char *str; ParamIndexType i = 0; ptr = __s_api_get_default_config(); if (ptr == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("No configuration information available for %s."), domainname == NULL ? "" : domainname); MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (errorp); } (void) memset((char *)configinfo, 0, sizeof (LineBuf)); for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) { str = __s_api_strValue(ptr, string, sizeof (string), i, NS_DOOR_FMT); if (str == NULL) continue; if (_print2buf(configinfo, str, 1) != 0) { (void) snprintf(errstr, sizeof (errstr), gettext("_print2buf: Out of memory.")); MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); __s_api_release_config(ptr); if (str != (char *)&string[0]) { free(str); str = NULL; } return (errorp); } if (str != (char *)&string[0]) { free(str); str = NULL; } } __s_api_release_config(ptr); return (NULL); } ns_ldap_error_t * __ns_ldap_DumpLdif(char *filename) { ns_config_t *ptr; char errstr[MAXERROR]; ns_ldap_error_t *errorp; char string[BUFSIZE]; char *str; FILE *fp; ParamIndexType i = 0; char *profile, *container, *base; ptr = __s_api_get_default_config(); if (ptr == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("No configuration information available.")); MKERROR(LOG_ERR, errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (errorp); } if (filename == NULL) { fp = stdout; } else { fp = fopen(filename, "wF"); if (fp == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("Unable to open filename %s for ldif " "dump (errno=%d)."), filename, errno); MKERROR(LOG_WARNING, errorp, NS_CONFIG_FILE, strdup(errstr), NULL); __s_api_release_config(ptr); return (errorp); } (void) fchmod(fileno(fp), 0444); } if (ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_ptype != CHARPTR || ptr->paramList[NS_LDAP_PROFILE_P].ns_ptype != CHARPTR) { (void) snprintf(errstr, sizeof (errstr), gettext("Required BaseDN and/or Profile name " "ldif fields not present")); MKERROR(LOG_WARNING, errorp, NS_CONFIG_FILE, strdup(errstr), NULL); __s_api_release_config(ptr); return (errorp); } profile = ptr->paramList[NS_LDAP_PROFILE_P].ns_pc; base = ptr->paramList[NS_LDAP_SEARCH_BASEDN_P].ns_pc; container = _PROFILE_CONTAINER; /* * Construct DN, but since this is the profile, there is no need * to worry about mapping. The profile itself can not be mapped */ (void) fprintf(fp, "dn: cn=%s,ou=%s,%s\n", profile, container, base); /* dump objectclass names */ if (ptr->version == NS_LDAP_V1) { (void) fprintf(fp, "ObjectClass: top\nObjectClass: %s\n", _PROFILE1_OBJECTCLASS); } else { (void) fprintf(fp, "ObjectClass: top\nObjectClass: %s\n", _PROFILE2_OBJECTCLASS); } /* For each parameter - construct value */ for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) { str = __s_api_strValue(ptr, string, BUFSIZ, i, NS_LDIF_FMT); if (str == NULL) continue; /* * don't dump binddn, bind password, or cert path as they * are not part of version 2 profiles */ if ((i != NS_LDAP_BINDDN_P) && (i != NS_LDAP_BINDPASSWD_P) && (i != NS_LDAP_HOST_CERTPATH_P)) (void) fprintf(fp, "%s\n", str); if (str != (char *)&string[0]) { free(str); str = NULL; } } if (filename != NULL) (void) fclose(fp); __s_api_release_config(ptr); return (NULL); } /* * This routine can process the configuration and/or * the credential files at the same time. * files is char *[3] = { "config", "cred", NULL }; */ static ns_ldap_error_t * __ns_ldap_DumpConfigFiles(char **files) { char *filename; int fi; int docred; ns_config_t *ptr; char string[BUFSIZE]; char *str; char errstr[MAXERROR]; ParamIndexType i = 0; FILE *fp; int rc; ns_ldap_error_t *errorp = NULL; struct stat buf; int cfgtype; boolean_t file_export_error = B_FALSE; ptr = __s_api_get_default_config(); if (ptr == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("No configuration information available.")); MKERROR(LOG_ERR, errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (errorp); } for (fi = 0; fi < 2; fi++) { docred = 0; filename = files[fi]; if (filename == NULL) continue; if (fi == 1) docred++; rc = stat(filename, &buf); fp = fopen(filename, "wF"); if (fp == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("Unable to open filename %s" " for configuration dump (%s)."), filename, strerror(errno)); MKERROR(LOG_ERR, errorp, NS_CONFIG_FILE, strdup(errstr), NULL); __s_api_release_config(ptr); return (errorp); } if (rc == 0) { if (fchmod(fileno(fp), buf.st_mode) != 0) { (void) snprintf(errstr, sizeof (errstr), gettext("Unable to set permissions for file" " %s for configuration dump (%s)."), filename, strerror(errno)); (void) fclose(fp); file_export_error = B_TRUE; break; } } else { if (fchmod(fileno(fp), 0400) != 0) { (void) snprintf(errstr, sizeof (errstr), gettext("Unable to set permissions for file" " %s for configuration dump (%s)."), filename, strerror(errno)); (void) fclose(fp); file_export_error = B_TRUE; break; } } if (fprintf(fp, "#\n# %s\n#\n", DONOTEDIT) < 0) { (void) snprintf(errstr, sizeof (errstr), gettext( "Writing to file %s for configuration dump failed " "(%s)."), filename, strerror(errno)); file_export_error = B_TRUE; } /* assume VERSION is set and it outputs first */ /* For each parameter - construct value */ for (i = 0; !file_export_error && (i <= NS_LDAP_MAX_PIT_P); i++) { cfgtype = __s_api_get_configtype(i); if ((docred == 0 && cfgtype == CREDCONFIG) || (docred == 1 && cfgtype != CREDCONFIG)) continue; str = __s_api_strValue(ptr, string, BUFSIZ, i, NS_FILE_FMT); if (str == NULL) continue; if (fprintf(fp, "%s\n", str) < 0) { (void) snprintf(errstr, sizeof (errstr), gettext("Writing to file %s for" "configuration dump failed (%s)."), filename, strerror(errno)); file_export_error = B_TRUE; } if (str != (char *)&string[0]) { free(str); str = NULL; } } if (fclose(fp) != 0) { /* Break if error already hit */ if (file_export_error) break; (void) snprintf(errstr, sizeof (errstr), gettext( "Writing to file %s for configuration dump failed " "during file close (%s)."), filename, strerror(errno)); file_export_error = B_TRUE; break; } } if (file_export_error) { MKERROR(LOG_ERR, errorp, NS_CONFIG_FILE, strdup(errstr), NULL); (void) unlink(filename); } __s_api_release_config(ptr); return (errorp); } ns_ldap_error_t * __ns_ldap_DumpConfiguration(char *file) { ns_ldap_error_t *ret; char *files[3]; files[0] = NULL; files[1] = NULL; files[2] = NULL; if (strcmp(file, NSCONFIGFILE) == 0) { files[0] = file; } else if (strcmp(file, NSCONFIGREFRESH) == 0) { files[0] = file; } else if (strcmp(file, NSCREDFILE) == 0) { files[1] = file; } else if (strcmp(file, NSCREDREFRESH) == 0) { files[1] = file; } ret = __ns_ldap_DumpConfigFiles(files); return (ret); } /* * ************************************************************** * Misc Routines * ************************************************************** */ ns_config_t * __ns_ldap_make_config(ns_ldap_result_t *result) { int l, m; char val[BUFSIZ]; char *attrname; ns_ldap_entry_t *entry; ns_ldap_attr_t *attr; char **attrval; ParamIndexType index; ns_config_t *ptr; ns_ldap_error_t *error = NULL; int firsttime; int prof_ver; ns_config_t *curr_ptr = NULL; char errstr[MAXERROR]; ns_ldap_error_t *errorp; if (result == NULL) return (NULL); if (result->entries_count > 1) { (void) snprintf(errstr, MAXERROR, gettext("Configuration Error: More than one profile " "found")); MKERROR(LOG_ERR, errorp, NS_PARSE_ERR, strdup(errstr), NULL); (void) __ns_ldap_freeError(&errorp); return (NULL); } ptr = __s_api_create_config(); if (ptr == NULL) return (NULL); curr_ptr = __s_api_get_default_config(); if (curr_ptr == NULL) { __s_api_destroy_config(ptr); return (NULL); } /* Check to see if the profile is version 1 or version 2 */ prof_ver = 1; entry = result->entry; for (l = 0; l < entry->attr_count; l++) { attr = entry->attr_pair[l]; attrname = attr->attrname; if (attrname == NULL) continue; if (strcasecmp(attrname, "objectclass") == 0) { for (m = 0; m < attr->value_count; m++) { if (strcasecmp(_PROFILE2_OBJECTCLASS, attr->attrvalue[m]) == 0) { prof_ver = 2; break; } } } } /* update the configuration to accept v1 or v2 attributes */ if (prof_ver == 1) { (void) strcpy(val, NS_LDAP_VERSION_1); (void) __ns_ldap_setParamValue(ptr, NS_LDAP_FILE_VERSION_P, val, &error); } else { (void) strcpy(val, NS_LDAP_VERSION_2); (void) __ns_ldap_setParamValue(ptr, NS_LDAP_FILE_VERSION_P, val, &error); } for (l = 0; l < entry->attr_count; l++) { attr = entry->attr_pair[l]; attrname = attr->attrname; if (attrname == NULL) continue; if (__s_api_get_profiletype(attrname, &index) != 0) continue; attrval = attr->attrvalue; switch (index) { case NS_LDAP_SEARCH_DN_P: case NS_LDAP_SERVICE_SEARCH_DESC_P: case NS_LDAP_ATTRIBUTEMAP_P: case NS_LDAP_OBJECTCLASSMAP_P: case NS_LDAP_SERVICE_CRED_LEVEL_P: case NS_LDAP_SERVICE_AUTH_METHOD_P: /* Multiple Value - insert 1 at a time */ for (m = 0; m < attr->value_count; m++) { (void) __ns_ldap_setParamValue(ptr, index, attrval[m], &error); } break; default: firsttime = 1; /* Single or Multiple Value */ val[0] = '\0'; for (m = 0; m < attr->value_count; m++) { if (firsttime == 1) { firsttime = 0; (void) strlcpy(val, attrval[m], sizeof (val)); } else { (void) strlcat(val, " ", sizeof (val)); (void) strlcat(val, attrval[m], sizeof (val)); } } (void) __ns_ldap_setParamValue(ptr, index, val, &error); break; } } if (ptr->version != NS_LDAP_V1) { if (curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_ptype == CHARPTR) { (void) __ns_ldap_setParamValue(ptr, NS_LDAP_BINDDN_P, curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_pc, &error); } if (curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_ptype == CHARPTR) { (void) __ns_ldap_setParamValue(ptr, NS_LDAP_BINDPASSWD_P, curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_pc, &error); } if (curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_ptype == CHARPTR) { (void) __ns_ldap_setParamValue(ptr, NS_LDAP_HOST_CERTPATH_P, curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_pc, &error); } } __s_api_release_config(curr_ptr); return (ptr); } /* * Download a profile into our internal structure. The calling application * needs to DumpConfig() to save the information to NSCONFIGFILE and NSCREDFILE * if desired. */ int __ns_ldap_download(const char *profile, char *addr, char *baseDN, ns_ldap_error_t **errorp) { char filter[BUFSIZ]; int rc; ns_ldap_result_t *result = NULL; ns_config_t *ptr = NULL; ns_config_t *new_ptr = NULL; char errstr[MAXERROR]; *errorp = NULL; if (baseDN == NULL) return (NS_LDAP_INVALID_PARAM); ptr = __s_api_get_default_config(); if (ptr == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("No configuration information available.")); MKERROR(LOG_ERR, *errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (NS_LDAP_CONFIG); } rc = __ns_ldap_setParamValue(ptr, NS_LDAP_SEARCH_BASEDN_P, baseDN, errorp); if (rc != NS_LDAP_SUCCESS) { __s_api_release_config(ptr); return (rc); } rc = __ns_ldap_setParamValue(ptr, NS_LDAP_SERVERS_P, addr, errorp); __s_api_release_config(ptr); if (rc != NS_LDAP_SUCCESS) return (rc); (void) snprintf(filter, sizeof (filter), _PROFILE_FILTER, _PROFILE1_OBJECTCLASS, _PROFILE2_OBJECTCLASS, profile); rc = __ns_ldap_list(_PROFILE_CONTAINER, (const char *)filter, NULL, NULL, NULL, 0, &result, errorp, NULL, NULL); if (rc != NS_LDAP_SUCCESS) return (rc); new_ptr = __ns_ldap_make_config(result); (void) __ns_ldap_freeResult(&result); if (new_ptr == NULL) return (NS_LDAP_OP_FAILED); rc = __s_api_crosscheck(new_ptr, errstr, B_FALSE); if (rc != NS_LDAP_SUCCESS) { __s_api_destroy_config(new_ptr); MKERROR(LOG_ERR, *errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (NS_LDAP_CONFIG); } __s_api_init_config(new_ptr); return (rc); } /* * ************************************************************** * Configuration Printing Routines * ************************************************************** */ /* * Yes the use of stdio is okay here because all we are doing is sending * output to stdout. This would not be necessary if we could get to the * configuration pointer outside this file. */ ns_ldap_error_t * __ns_ldap_print_config(int verbose) { ns_config_t *ptr; char errstr[MAXERROR]; ns_ldap_error_t *errorp; char string[BUFSIZE]; char *str; int i; ptr = __s_api_get_default_config(); if (ptr == NULL) { errorp = __ns_ldap_LoadConfiguration(); if (errorp != NULL) return (errorp); ptr = __s_api_get_default_config(); if (ptr == NULL) { (void) snprintf(errstr, sizeof (errstr), gettext("No configuration information.")); MKERROR(LOG_WARNING, errorp, NS_CONFIG_NOTLOADED, strdup(errstr), NULL); return (errorp); } } if (verbose && (ptr->domainName != NULL)) { (void) fputs("ptr->domainName ", stdout); (void) fputs(ptr->domainName, stdout); (void) putchar('\n'); } /* For each parameter - construct value */ for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) { /* * Version 1 skipped this entry because: * * don't print default cache TTL for now since * we don't store it in the ldap_client_file. */ if ((i == NS_LDAP_CACHETTL_P) && (ptr->version == NS_LDAP_V1)) continue; str = __s_api_strValue(ptr, string, BUFSIZ, i, NS_FILE_FMT); if (str == NULL) continue; if (verbose) (void) putchar('\t'); (void) fprintf(stdout, "%s\n", str); if (str != (char *)&string[0]) { free(str); str = NULL; } } __s_api_release_config(ptr); return (NULL); }