diff options
Diffstat (limited to 'usr/src/lib/libc/port/gen/crypt.c')
-rw-r--r-- | usr/src/lib/libc/port/gen/crypt.c | 1156 |
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); +} |