summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/port/gen/crypt.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libc/port/gen/crypt.c')
-rw-r--r--usr/src/lib/libc/port/gen/crypt.c1156
1 files changed, 1156 insertions, 0 deletions
diff --git a/usr/src/lib/libc/port/gen/crypt.c b/usr/src/lib/libc/port/gen/crypt.c
new file mode 100644
index 0000000000..f24c732924
--- /dev/null
+++ b/usr/src/lib/libc/port/gen/crypt.c
@@ -0,0 +1,1156 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#pragma weak crypt = _crypt
+#pragma weak encrypt = _encrypt
+#pragma weak setkey = _setkey
+
+#include "synonyms.h"
+#include "mtlib.h"
+#include <synch.h>
+#include <thread.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <crypt.h>
+#include <libc.h>
+#include "tsd.h"
+
+#define CRYPT_ALGORITHMS_ALLOW "CRYPT_ALGORITHMS_ALLOW"
+#define CRYPT_ALGORITHMS_DEPRECATE "CRYPT_ALGORITHMS_DEPRECATE"
+#define CRYPT_DEFAULT "CRYPT_DEFAULT"
+#define CRYPT_UNIX "__unix__"
+
+#define CRYPT_CONFFILE "/etc/security/crypt.conf"
+#define POLICY_CONF_FILE "/etc/security/policy.conf"
+
+#define CRYPT_CONFLINELENGTH 1024
+
+#define CRYPT_MODULE_ISA "/$ISA/"
+#ifdef _LP64
+#define CRYPT_MODULE_DIR "/usr/lib/security/64/"
+#define CRYPT_ISA_DIR "/64/"
+#else /* !_LP64 */
+#define CRYPT_MODULE_DIR "/usr/lib/security/"
+#define CRYPT_ISA_DIR "/"
+#endif /* _LP64 */
+
+/*
+ * MAX_ALGNAME_LEN:
+ *
+ * In practical terms this is probably never any bigger than about 10, but...
+ *
+ * It has to fix the encrypted password filed of struct spwd it is
+ * theoretically the maximum length of the cipher minus the magic $ sign.
+ * Though that would be unexpected.
+ * Since it also has to fit in crypt.conf it is CRYPT_CONFLINELENGTH
+ * minus the path to the module and the minimum white space.
+ *
+ * CRYPT_MAXCIPHERTEXTLEN is defined in crypt.h and is smaller than
+ * CRYPT_CONFLINELENGTH, and probably always will be.
+ */
+#define MAX_ALGNAME_LEN (CRYPT_MAXCIPHERTEXTLEN - 1)
+
+struct crypt_alg_s {
+ void *a_libhandle;
+ char *(*a_genhash)(char *, const size_t, const char *,
+ const char *, const char **);
+ char *(*a_gensalt)(char *, const size_t,
+ const char *, const struct passwd *, const char **);
+ char **a_params;
+ int a_nparams;
+};
+
+struct crypt_policy_s {
+ char *cp_default;
+ char *cp_allow;
+ char *cp_deny;
+};
+
+enum crypt_policy_error_e {
+ CPE_BOTH = 1,
+ CPE_MULTI
+};
+
+static struct crypt_policy_s *getcryptpolicy(void);
+static void free_crypt_policy(struct crypt_policy_s *policy);
+static struct crypt_alg_s *getalgbyname(const char *algname, boolean_t *found);
+static void free_crypt_alg(struct crypt_alg_s *alg);
+static char *getalgfromsalt(const char *salt);
+static boolean_t alg_valid(const char *algname,
+ const struct crypt_policy_s *policy);
+static char *isa_path(const char *path);
+
+static char *_unix_crypt(const char *pw, const char *salt, char *iobuf);
+static char *_unix_crypt_gensalt(char *gsbuffer, size_t gsbufflen,
+ const char *oldpuresalt, const struct passwd *userinfo,
+ const char *params[]);
+
+
+/*
+ * crypt - string encoding function
+ *
+ * This function encodes strings in a suitable for for secure storage
+ * as passwords. It generates the password hash given the plaintext and salt.
+ *
+ * If the first character of salt is "$" then we use crypt.conf(4) to
+ * determine which plugin to use and run the crypt_genhash_impl(3c) function
+ * from it.
+ * Otherwise we use the old unix algorithm.
+ *
+ * RETURN VALUES
+ * On Success we return a pointer to the encoded string. The
+ * return value points to thread specific static data and should NOT
+ * be passed free(3c).
+ * On failure we return NULL and set errno to one of:
+ * EINVAL, ELIBACC, ENOMEM, ENOSYS.
+ */
+char *
+crypt(const char *plaintext, const char *salt)
+{
+ struct crypt_alg_s *alg;
+ char *ctbuffer;
+ char *ciphertext;
+ char *algname;
+ boolean_t found;
+
+ ctbuffer = tsdalloc(_T_CRYPT, CRYPT_MAXCIPHERTEXTLEN, NULL);
+ if (ctbuffer == NULL)
+ return (NULL);
+ bzero(ctbuffer, CRYPT_MAXCIPHERTEXTLEN);
+
+ /*
+ * '$' is never a possible salt char with the traditional unix
+ * algorithm. If the salt passed in is NULL or the first char
+ * of the salt isn't a $ then do the traditional thing.
+ * We also do the traditional thing if the salt is only 1 char.
+ */
+ if (salt == NULL || salt[0] != '$' || strlen(salt) == 1) {
+ return (_unix_crypt(plaintext, salt, ctbuffer));
+ }
+
+ /*
+ * Find the algorithm name from the salt and look it up in
+ * crypt.conf(4) to find out what shared object to use.
+ * If we can't find it in crypt.conf then getalgbyname would
+ * have returned with found = B_FALSE so we use the unix algorithm.
+ * If alg is NULL but found = B_TRUE then there is a problem with
+ * the plugin so we fail leaving errno set to what getalgbyname()
+ * set it to or EINVAL it if wasn't set.
+ */
+ if ((algname = getalgfromsalt(salt)) == NULL) {
+ return (NULL);
+ }
+
+ errno = 0;
+ alg = getalgbyname(algname, &found);
+ if ((alg == NULL) || !found) {
+ if (errno == 0)
+ errno = EINVAL;
+ ciphertext = NULL;
+ goto cleanup;
+ } else if (!found) {
+ ciphertext = _unix_crypt(plaintext, salt, ctbuffer);
+ } else {
+ ciphertext = alg->a_genhash(ctbuffer, CRYPT_MAXCIPHERTEXTLEN,
+ plaintext, salt, (const char **)alg->a_params);
+ }
+
+cleanup:
+ free_crypt_alg(alg);
+ if (algname != NULL)
+ free(algname);
+
+ return (ciphertext);
+}
+
+/*
+ * crypt_gensalt - generate salt string for string encoding
+ *
+ * This function generates the salt string pased to crypt(3c).
+ * If oldsalt is NULL, the use the default algorithm.
+ * Other wise check the policy in policy.conf to ensure that it is
+ * either still allowed or not deprecated.
+ *
+ * RETURN VALUES
+ * Return a pointer to the new salt, the caller is responsible
+ * for using free(3c) on the return value.
+ * Returns NULL on error and sets errno to one of:
+ * EINVAL, ELIBACC, ENOMEM
+ */
+char *
+crypt_gensalt(const char *oldsalt, const struct passwd *userinfo)
+{
+ struct crypt_alg_s *alg = NULL;
+ struct crypt_policy_s *policy = NULL;
+ char *newsalt = NULL;
+ char *gsbuffer;
+ char *algname = NULL;
+ boolean_t found;
+
+ gsbuffer = calloc(CRYPT_MAXCIPHERTEXTLEN, sizeof (char *));
+ if (gsbuffer == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+
+ policy = getcryptpolicy();
+ if (policy == NULL) {
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ algname = getalgfromsalt(oldsalt);
+ if (!alg_valid(algname, policy)) {
+ free(algname);
+ algname = strdup(policy->cp_default);
+ }
+
+ if (strcmp(algname, CRYPT_UNIX) == 0) {
+ newsalt = _unix_crypt_gensalt(gsbuffer, CRYPT_MAXCIPHERTEXTLEN,
+ oldsalt, userinfo, NULL);
+ } else {
+ errno = 0;
+ alg = getalgbyname(algname, &found);
+ if (alg == NULL || !found) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto cleanup;
+ }
+ newsalt = alg->a_gensalt(gsbuffer, CRYPT_MAXCIPHERTEXTLEN,
+ oldsalt, userinfo, (const char **)alg->a_params);
+ }
+
+cleanup:
+ free_crypt_policy(policy);
+ free_crypt_alg(alg);
+ if (newsalt == NULL && gsbuffer != NULL)
+ free(gsbuffer);
+ if (algname != NULL)
+ free(algname);
+
+ return (newsalt);
+}
+
+/*
+ * ===========================================================================
+ * The remainder of this file contains internal interfaces for
+ * the implementation of crypt(3c) and crypt_gensalt(3c)
+ * ===========================================================================
+ */
+
+
+/*
+ * getalgfromsalt - extract the algorithm name from the salt string
+ */
+static char *
+getalgfromsalt(const char *salt)
+{
+ char algname[CRYPT_MAXCIPHERTEXTLEN];
+ int i;
+ int j;
+
+ if (salt == NULL || strlen(salt) > CRYPT_MAXCIPHERTEXTLEN)
+ return (NULL);
+ /*
+ * Salts are in this format:
+ * $<algname>[,var=val,[var=val ...][$puresalt]$<ciphertext>
+ *
+ * The only bit we need to worry about here is extracting the
+ * name which is the string between the first "$" and the first
+ * of "," or second "$".
+ */
+ if (salt[0] != '$') {
+ return (strdup(CRYPT_UNIX));
+ }
+
+ i = 1;
+ j = 0;
+ while (salt[i] != '\0' && salt[i] != '$' && salt[i] != ',') {
+ algname[j] = salt[i];
+ i++;
+ j++;
+ }
+ if (j == 0)
+ return (NULL);
+
+ algname[j] = '\0';
+
+ return (strdup(algname));
+}
+
+
+/*
+ * log_invalid_policy - syslog helper
+ */
+static void
+log_invalid_policy(enum crypt_policy_error_e error, char *value)
+{
+ switch (error) {
+ case CPE_BOTH:
+ syslog(LOG_AUTH | LOG_ERR,
+ "crypt(3c): %s contains both %s and %s; only one may be "
+ "specified, using first entry in file.", POLICY_CONF_FILE,
+ CRYPT_ALGORITHMS_ALLOW, CRYPT_ALGORITHMS_DEPRECATE);
+ break;
+ case CPE_MULTI:
+ syslog(LOG_AUTH | LOG_ERR,
+ "crypt(3c): %s contains multiple %s entries;"
+ "using first entry file.", POLICY_CONF_FILE, value);
+ break;
+ }
+}
+
+static char *
+getval(const char *ival)
+{
+ char *tmp;
+ char *oval;
+ int off;
+
+ if (ival == NULL)
+ return (NULL);
+
+ if ((tmp = strchr(ival, '=')) == NULL)
+ return (NULL);
+
+ oval = strdup(tmp + 1); /* everything after the "=" */
+ if (oval == NULL)
+ return (NULL);
+ off = strlen(oval) - 1;
+ if (off < 0) {
+ free(oval);
+ return (NULL);
+ }
+ if (oval[off] == '\n')
+ oval[off] = '\0';
+
+ return (oval);
+}
+
+/*
+ * getcryptpolicy - read /etc/security/policy.conf into a crypt_policy_s
+ */
+static struct crypt_policy_s *
+getcryptpolicy(void)
+{
+ FILE *pconf;
+ char line[BUFSIZ];
+ struct crypt_policy_s *policy;
+
+ if ((pconf = fopen(POLICY_CONF_FILE, "r")) == NULL) {
+ return (NULL);
+ }
+
+ policy = malloc(sizeof (struct crypt_policy_s));
+ if (policy == NULL) {
+ return (NULL);
+ }
+ policy->cp_default = NULL;
+ policy->cp_allow = NULL;
+ policy->cp_deny = NULL;
+
+ while (!feof(pconf) &&
+ (fgets(line, sizeof (line), pconf) != NULL)) {
+ if (strncasecmp(CRYPT_DEFAULT, line,
+ strlen(CRYPT_DEFAULT)) == 0) {
+ if (policy->cp_default != NULL) {
+ log_invalid_policy(CPE_MULTI, CRYPT_DEFAULT);
+ } else {
+ policy->cp_default = getval(line);
+ }
+ }
+ if (strncasecmp(CRYPT_ALGORITHMS_ALLOW, line,
+ strlen(CRYPT_ALGORITHMS_ALLOW)) == 0) {
+ if (policy->cp_deny != NULL) {
+ log_invalid_policy(CPE_BOTH, NULL);
+ } else if (policy->cp_allow != NULL) {
+ log_invalid_policy(CPE_MULTI,
+ CRYPT_ALGORITHMS_ALLOW);
+ } else {
+ policy->cp_allow = getval(line);
+ }
+ }
+ if (strncasecmp(CRYPT_ALGORITHMS_DEPRECATE, line,
+ strlen(CRYPT_ALGORITHMS_DEPRECATE)) == 0) {
+ if (policy->cp_allow != NULL) {
+ log_invalid_policy(CPE_BOTH, NULL);
+ } else if (policy->cp_deny != NULL) {
+ log_invalid_policy(CPE_MULTI,
+ CRYPT_ALGORITHMS_DEPRECATE);
+ } else {
+ policy->cp_deny = getval(line);
+ }
+ }
+ }
+ (void) fclose(pconf);
+
+ if (policy->cp_default == NULL) {
+ policy->cp_default = strdup(CRYPT_UNIX);
+ if (policy->cp_default == NULL)
+ free_crypt_policy(policy);
+ }
+
+ return (policy);
+}
+
+
+/*
+ * alg_valid - is this algorithm valid given the policy ?
+ */
+static boolean_t
+alg_valid(const char *algname, const struct crypt_policy_s *policy)
+{
+ char *lasts;
+ char *list;
+ char *entry;
+ boolean_t allowed = B_FALSE;
+
+ if ((algname == NULL) || (policy == NULL)) {
+ return (B_FALSE);
+ }
+
+ if (strcmp(algname, policy->cp_default) == 0) {
+ return (B_TRUE);
+ }
+
+ if (policy->cp_deny != NULL) {
+ list = policy->cp_deny;
+ allowed = B_FALSE;
+ } else if (policy->cp_allow != NULL) {
+ list = policy->cp_allow;
+ allowed = B_TRUE;
+ } else {
+ /*
+ * Neither of allow or deny policies are set so anything goes.
+ */
+ return (B_TRUE);
+ }
+ lasts = list;
+ while ((entry = strtok_r(NULL, ",", &lasts)) != NULL) {
+ if (strcmp(entry, algname) == 0) {
+ return (allowed);
+ }
+ }
+
+ return (!allowed);
+}
+
+/*
+ * getalgbyname - read crypt.conf(4) looking for algname
+ *
+ * RETURN VALUES
+ * On error NULL and errno is set
+ * On success the alg details including an open handle to the lib
+ * If crypt.conf(4) is okay but algname doesn't exist in it then
+ * return NULL the caller should then use the default algorithm
+ * as per the policy.
+ */
+static struct crypt_alg_s *
+getalgbyname(const char *algname, boolean_t *found)
+{
+ struct stat stb;
+ int configfd;
+ FILE *fconf = NULL;
+ struct crypt_alg_s *alg = NULL;
+ char line[CRYPT_CONFLINELENGTH];
+ int linelen = 0;
+ int lineno = 0;
+ char *pathname = NULL;
+ char *lasts = NULL;
+ char *token = NULL;
+
+ *found = B_FALSE;
+ if ((algname == NULL) || (strcmp(algname, CRYPT_UNIX) == 0)) {
+ return (NULL);
+ }
+
+ if ((configfd = open(CRYPT_CONFFILE, O_RDONLY)) == -1) {
+ syslog(LOG_ALERT, "crypt: open(%s) failed: %s",
+ CRYPT_CONFFILE, strerror(errno));
+ return (NULL);
+ }
+
+ /*
+ * Stat the file so we can check modes and ownerships
+ */
+ if (fstat(configfd, &stb) < 0) {
+ syslog(LOG_ALERT, "crypt: stat(%s) failed: %s",
+ CRYPT_CONFFILE, strerror(errno));
+ goto cleanup;
+ }
+
+ /*
+ * Check the ownership of the file
+ */
+ if (stb.st_uid != (uid_t)0) {
+ syslog(LOG_ALERT,
+ "crypt: Owner of %s is not root", CRYPT_CONFFILE);
+ goto cleanup;
+ }
+
+ /*
+ * Check the modes on the file
+ */
+ if (stb.st_mode & S_IWGRP) {
+ syslog(LOG_ALERT,
+ "crypt: %s writable by group", CRYPT_CONFFILE);
+ goto cleanup;
+ }
+ if (stb.st_mode & S_IWOTH) {
+ syslog(LOG_ALERT,
+ "crypt: %s writable by world", CRYPT_CONFFILE);
+ goto cleanup;
+ }
+
+ if ((fconf = fdopen(configfd, "r")) == NULL) {
+ syslog(LOG_ALERT, "crypt: fdopen(%d) failed: %s",
+ configfd, strerror(errno));
+ goto cleanup;
+ }
+
+ /*
+ * /etc/security/crypt.conf has 3 fields:
+ * <algname> <pathname> [<name[=val]>[<name[=val]>]]
+ */
+ errno = 0;
+ while (!(*found) &&
+ ((fgets(line, sizeof (line), fconf) != NULL) && !feof(fconf))) {
+ lineno++;
+ /*
+ * Skip over comments
+ */
+ if ((line[0] == '#') || (line[0] == '\n')) {
+ continue;
+ }
+
+ linelen = strlen(line);
+ line[--linelen] = '\0'; /* chop the trailing \n */
+
+ token = strtok_r(line, " \t", &lasts);
+ if (token == NULL) {
+ continue;
+ }
+ if (strcmp(token, algname) == 0) {
+ *found = B_TRUE;
+ }
+ }
+ if (!found) {
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ token = strtok_r(NULL, " \t", &lasts);
+ if (token == NULL) {
+ /*
+ * Broken config file
+ */
+ syslog(LOG_ALERT, "crypt(3c): %s may be corrupt at line %d",
+ CRYPT_CONFFILE, lineno);
+ *found = B_FALSE;
+ errno = EINVAL;
+ goto cleanup;
+ }
+
+ if ((pathname = isa_path(token)) == NULL) {
+ if (errno != ENOMEM)
+ errno = EINVAL;
+ *found = B_FALSE;
+ goto cleanup;
+ }
+
+ if ((alg = malloc(sizeof (struct crypt_alg_s))) == NULL) {
+ *found = B_FALSE;
+ goto cleanup;
+ }
+ alg->a_libhandle = NULL;
+ alg->a_genhash = NULL;
+ alg->a_gensalt = NULL;
+ alg->a_params = NULL;
+ alg->a_nparams = 0;
+
+ /*
+ * The rest of the line is module specific params, space
+ * seprated. We wait until after we have checked the module is
+ * valid before parsing them into a_params, this saves us
+ * having to free them later if there is a problem.
+ */
+ if ((alg->a_libhandle = dlopen(pathname, RTLD_NOW)) == NULL) {
+ syslog(LOG_ERR, "crypt(3c) unable to dlopen %s: %s",
+ pathname, dlerror());
+ errno = ELIBACC;
+ *found = B_FALSE;
+ goto cleanup;
+ }
+
+ alg->a_genhash =
+ (char *(*)())dlsym(alg->a_libhandle, "crypt_genhash_impl");
+ if (alg->a_genhash == NULL) {
+ syslog(LOG_ERR, "crypt(3c) unable to find cryp_genhash_impl"
+ "symbol in %s: %s", pathname, dlerror());
+ errno = ELIBACC;
+ *found = B_FALSE;
+ goto cleanup;
+ }
+ alg->a_gensalt =
+ (char *(*)())dlsym(alg->a_libhandle, "crypt_gensalt_impl");
+ if (alg->a_gensalt == NULL) {
+ syslog(LOG_ERR, "crypt(3c) unable to find crypt_gensalt_impl"
+ "symbol in %s: %s", pathname, dlerror());
+ errno = ELIBACC;
+ *found = B_FALSE;
+ goto cleanup;
+ }
+
+ /*
+ * We have a good module so build the a_params if we have any.
+ * Count how much space we need first and then allocate an array
+ * to hold that many module params.
+ */
+ if (lasts != NULL) {
+ int nparams = 0;
+ char *tparams;
+ char *tplasts;
+
+ if ((tparams = strdup(lasts)) == NULL) {
+ *found = B_FALSE;
+ goto cleanup;
+ }
+
+ (void) strtok_r(tparams, " \t", &tplasts);
+ do {
+ nparams++;
+ } while (strtok_r(NULL, " \t", &tplasts) != NULL);
+ free(tparams);
+
+ alg->a_params = calloc(nparams + 1, sizeof (char *));
+ if (alg->a_params == NULL) {
+ *found = B_FALSE;
+ goto cleanup;
+ }
+
+ while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) {
+ alg->a_params[alg->a_nparams++] = token;
+ }
+ }
+
+cleanup:
+ if (*found == B_FALSE) {
+ free_crypt_alg(alg);
+ alg = NULL;
+ }
+
+ if (pathname != NULL) {
+ free(pathname);
+ }
+
+ if (fconf != NULL) {
+ (void) fclose(fconf);
+ } else {
+ (void) close(configfd);
+ }
+
+ return (alg);
+}
+
+static void
+free_crypt_alg(struct crypt_alg_s *alg)
+{
+ if (alg == NULL)
+ return;
+
+ if (alg->a_libhandle != NULL) {
+ (void) dlclose(alg->a_libhandle);
+ }
+ if (alg->a_nparams != NULL) {
+ free(alg->a_params);
+ }
+ free(alg);
+}
+
+static void
+free_crypt_policy(struct crypt_policy_s *policy)
+{
+ if (policy == NULL)
+ return;
+
+ if (policy->cp_default != NULL) {
+ bzero(policy->cp_default, strlen(policy->cp_default));
+ free(policy->cp_default);
+ policy->cp_default = NULL;
+ }
+
+ if (policy->cp_allow != NULL) {
+ bzero(policy->cp_allow, strlen(policy->cp_allow));
+ free(policy->cp_allow);
+ policy->cp_allow = NULL;
+ }
+
+ if (policy->cp_deny != NULL) {
+ bzero(policy->cp_deny, strlen(policy->cp_deny));
+ free(policy->cp_deny);
+ policy->cp_deny = NULL;
+ }
+
+ free(policy);
+}
+
+
+/*
+ * isa_path - prepend the default dir or patch up the $ISA in path
+ * Caller is responsible for calling free(3c) on the result.
+ */
+static char *
+isa_path(const char *path)
+{
+ char *ret = NULL;
+
+ if ((path == NULL) || (strlen(path) > PATH_MAX)) {
+ return (NULL);
+ }
+
+ ret = calloc(PATH_MAX, sizeof (char));
+
+ /*
+ * Module path doesn't start with "/" then prepend
+ * the default search path CRYPT_MODULE_DIR (/usr/lib/security/$ISA)
+ */
+ if (path[0] != '/') {
+ if (snprintf(ret, PATH_MAX, "%s%s", CRYPT_MODULE_DIR,
+ path) > PATH_MAX) {
+ free(ret);
+ return (NULL);
+ }
+ } else { /* patch up $ISA */
+ char *isa;
+
+ if ((isa = strstr(path, CRYPT_MODULE_ISA)) != NULL) {
+ *isa = '\0';
+ isa += strlen(CRYPT_MODULE_ISA);
+ if (snprintf(ret, PATH_MAX, "%s%s%s", path,
+ CRYPT_ISA_DIR, isa) > PATH_MAX) {
+ free(ret);
+ return (NULL);
+ }
+ } else {
+ free(ret);
+ ret = strdup(path);
+ }
+ }
+
+ return (ret);
+}
+
+
+/*ARGSUSED*/
+static char *
+_unix_crypt_gensalt(char *gsbuffer,
+ size_t gsbufflen,
+ const char *oldpuresalt,
+ const struct passwd *userinfo,
+ const char *argv[])
+{
+ static const char saltchars[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ struct timeval tv;
+
+ gettimeofday(&tv, (void *) 0);
+ srand48(tv.tv_sec ^ tv.tv_usec);
+ gsbuffer[0] = saltchars[lrand48() % 64]; /* lrand48() is MT-SAFE */
+ gsbuffer[1] = saltchars[lrand48() % 64]; /* lrand48() is MT-SAFE */
+ gsbuffer[2] = '\0';
+
+ return (gsbuffer);
+}
+
+/*
+ * The rest of the code below comes from the old crypt.c and is the
+ * implementation of the hardwired/fallback traditional algorithm
+ * It has been otimized to take better advantage of MT features.
+ *
+ * It is included here to reduce the overhead of dlopen()
+ * for the common case.
+ */
+
+
+/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+
+/*
+ * This program implements a data encryption algorithm to encrypt passwords.
+ */
+
+static mutex_t crypt_lock = DEFAULTMUTEX;
+#define TSDBUFSZ (66 + 16)
+
+static const char IP[] = {
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7,
+};
+
+static const char FP[] = {
+ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25,
+};
+
+static const char PC1_C[] = {
+ 57, 49, 41, 33, 25, 17, 9,
+ 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27,
+ 19, 11, 3, 60, 52, 44, 36,
+};
+
+static const char PC1_D[] = {
+ 63, 55, 47, 39, 31, 23, 15,
+ 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29,
+ 21, 13, 5, 28, 20, 12, 4,
+};
+
+static const char shifts[] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1,
+};
+
+static const char PC2_C[] = {
+ 14, 17, 11, 24, 1, 5,
+ 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8,
+ 16, 7, 27, 20, 13, 2,
+};
+
+static const char PC2_D[] = {
+ 41, 52, 31, 37, 47, 55,
+ 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53,
+ 46, 42, 50, 36, 29, 32,
+};
+
+static char C[28];
+static char D[28];
+static char *KS;
+
+static char E[48];
+static const char e2[] = {
+ 32, 1, 2, 3, 4, 5,
+ 4, 5, 6, 7, 8, 9,
+ 8, 9, 10, 11, 12, 13,
+ 12, 13, 14, 15, 16, 17,
+ 16, 17, 18, 19, 20, 21,
+ 20, 21, 22, 23, 24, 25,
+ 24, 25, 26, 27, 28, 29,
+ 28, 29, 30, 31, 32, 1,
+};
+
+/*
+ * The KS array (768 bytes) is allocated once, and only if
+ * one of _unix_crypt(), encrypt() or setkey() is called.
+ * The complexity below is due to the fact that calloc()
+ * must not be called while holding any locks.
+ */
+static int
+allocate_KS(void)
+{
+ char *ks;
+ int failed;
+ int assigned;
+
+ if (KS != NULL) /* already allocated */
+ return (0);
+
+ ks = calloc(16, 48 * sizeof (char));
+ failed = 0;
+ lmutex_lock(&crypt_lock);
+ if (KS != NULL) { /* someone else got here first */
+ assigned = 0;
+ } else {
+ assigned = 1;
+ if ((KS = ks) == NULL) /* calloc() failed */
+ failed = 1;
+ }
+ lmutex_unlock(&crypt_lock);
+ if (!assigned)
+ free(ks);
+ return (failed);
+}
+
+static void
+unlocked_setkey(const char *key)
+{
+ int i, j, k;
+ char t;
+
+ for (i = 0; i < 28; i++) {
+ C[i] = key[PC1_C[i]-1];
+ D[i] = key[PC1_D[i]-1];
+ }
+ for (i = 0; i < 16; i++) {
+ for (k = 0; k < shifts[i]; k++) {
+ t = C[0];
+ for (j = 0; j < 28-1; j++)
+ C[j] = C[j+1];
+ C[27] = t;
+ t = D[0];
+ for (j = 0; j < 28-1; j++)
+ D[j] = D[j+1];
+ D[27] = t;
+ }
+ for (j = 0; j < 24; j++) {
+ int index = i * 48;
+
+ *(KS+index+j) = C[PC2_C[j]-1];
+ *(KS+index+j+24) = D[PC2_D[j]-28-1];
+ }
+ }
+ for (i = 0; i < 48; i++)
+ E[i] = e2[i];
+}
+
+static const char S[8][64] = {
+ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
+
+ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
+
+ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
+
+ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
+
+ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
+
+ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
+
+ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
+
+ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11,
+};
+
+static const char P[] = {
+ 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25,
+};
+
+static char L[64];
+static char tempL[32];
+static char f[32];
+
+static char preS[48];
+
+/*ARGSUSED*/
+static void
+unlocked_encrypt(char *block, int fake)
+{
+ int i;
+ int t, j, k;
+ char *R = &L[32];
+
+ for (j = 0; j < 64; j++)
+ L[j] = block[IP[j]-1];
+ for (i = 0; i < 16; i++) {
+ int index = i * 48;
+
+ for (j = 0; j < 32; j++)
+ tempL[j] = R[j];
+ for (j = 0; j < 48; j++)
+ preS[j] = R[E[j]-1] ^ *(KS+index+j);
+ for (j = 0; j < 8; j++) {
+ t = 6 * j;
+ k = S[j][(preS[t+0]<<5)+
+ (preS[t+1]<<3)+
+ (preS[t+2]<<2)+
+ (preS[t+3]<<1)+
+ (preS[t+4]<<0)+
+ (preS[t+5]<<4)];
+ t = 4*j;
+ f[t+0] = (k>>3)&01;
+ f[t+1] = (k>>2)&01;
+ f[t+2] = (k>>1)&01;
+ f[t+3] = (k>>0)&01;
+ }
+ for (j = 0; j < 32; j++)
+ R[j] = L[j] ^ f[P[j]-1];
+ for (j = 0; j < 32; j++)
+ L[j] = tempL[j];
+ }
+ for (j = 0; j < 32; j++) {
+ t = L[j];
+ L[j] = R[j];
+ R[j] = (char)t;
+ }
+ for (j = 0; j < 64; j++)
+ block[j] = L[FP[j]-1];
+}
+
+char *
+_unix_crypt(const char *pw, const char *salt, char *iobuf)
+{
+ int c, i, j;
+ char temp;
+ char *block;
+
+ block = iobuf + 16;
+
+ if (iobuf == 0) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if (allocate_KS() != 0)
+ return (NULL);
+ lmutex_lock(&crypt_lock);
+ for (i = 0; i < 66; i++)
+ block[i] = 0;
+ for (i = 0; (c = *pw) != '\0' && i < 64; pw++) {
+ for (j = 0; j < 7; j++, i++)
+ block[i] = (c>>(6-j)) & 01;
+ i++;
+ }
+
+ unlocked_setkey(block);
+
+ for (i = 0; i < 66; i++)
+ block[i] = 0;
+
+ for (i = 0; i < 2; i++) {
+ c = *salt++;
+ iobuf[i] = (char)c;
+ if (c > 'Z')
+ c -= 6;
+ if (c > '9')
+ c -= 7;
+ c -= '.';
+ for (j = 0; j < 6; j++) {
+ if ((c>>j) & 01) {
+ temp = E[6*i+j];
+ E[6*i+j] = E[6*i+j+24];
+ E[6*i+j+24] = temp;
+ }
+ }
+ }
+
+ for (i = 0; i < 25; i++)
+ unlocked_encrypt(block, 0);
+
+ lmutex_unlock(&crypt_lock);
+ for (i = 0; i < 11; i++) {
+ c = 0;
+ for (j = 0; j < 6; j++) {
+ c <<= 1;
+ c |= block[6*i+j];
+ }
+ c += '.';
+ if (c > '9')
+ c += 7;
+ if (c > 'Z')
+ c += 6;
+ iobuf[i+2] = (char)c;
+ }
+ iobuf[i+2] = 0;
+ if (iobuf[1] == 0)
+ iobuf[1] = iobuf[0];
+ return (iobuf);
+}
+
+
+/*ARGSUSED*/
+void
+encrypt(char *block, int fake)
+{
+ if (fake != 0) {
+ errno = ENOSYS;
+ return;
+ }
+ if (allocate_KS() != 0)
+ return;
+ lmutex_lock(&crypt_lock);
+ unlocked_encrypt(block, fake);
+ lmutex_unlock(&crypt_lock);
+}
+
+
+void
+setkey(const char *key)
+{
+ if (allocate_KS() != 0)
+ return;
+ lmutex_lock(&crypt_lock);
+ unlocked_setkey(key);
+ lmutex_unlock(&crypt_lock);
+}