1
0
Fork 0
pam/modules/pam_unix/bigcrypt.c
Daniel Baumann 82f0236850
Adding upstream version 1.7.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 06:53:43 +02:00

169 lines
4.5 KiB
C

/*
* This function implements the "bigcrypt" algorithm specifically for
* Linux-PAM.
*
* This algorithm is algorithm 0 (default) shipped with the C2 secure
* implementation of Digital UNIX.
*
* Disclaimer: This work is not based on the source code to Digital
* UNIX, nor am I connected to Digital Equipment Corp, in any way
* other than as a customer. This code is based on published
* interfaces and reasonable guesswork.
*
* Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
* characters or less. Each block is encrypted using the standard UNIX
* libc crypt function. The result of the encryption for one block
* provides the salt for the succeeding block.
*
* Restrictions: The buffer used to hold the encrypted result is
* statically allocated. (see MAX_PASS_LEN below). This is necessary,
* as the returned pointer points to "static data that are overwritten
* by each call", (XPG3: XSI System Interface + Headers pg 109), and
* this is a drop in replacement for crypt();
*
* Andy Phillips <atp@mssl.ucl.ac.uk>
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <security/_pam_macros.h>
#include "pam_inline.h"
#ifdef HAVE_CRYPT_H
#include <crypt.h>
#endif
#include "bigcrypt.h"
/*
* Max cleartext password length in segments of 8 characters this
* function can deal with (16 segments of 8 chars= max 128 character
* password).
*/
#define MAX_PASS_LEN 16
#define SEGMENT_SIZE 8
#define SALT_SIZE 2
#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
#define ESEGMENT_SIZE 11
#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
char *bigcrypt(const char *key, const char *salt)
{
char *dec_c2_cryptbuf;
#ifdef HAVE_CRYPT_R
struct crypt_data *cdata;
#endif
size_t keylen, n_seg, j;
char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
char keybuf[KEYBUF_SIZE + 1] = {};
D(("called with key='%s', salt='%s'.", key, salt));
/* reset arrays */
dec_c2_cryptbuf = calloc(1, CBUF_SIZE);
if (!dec_c2_cryptbuf) {
return NULL;
}
#ifdef HAVE_CRYPT_R
cdata = calloc(1, sizeof(*cdata));
if(!cdata) {
free(dec_c2_cryptbuf);
return NULL;
}
#endif
/* fill KEYBUF_SIZE with key */
strncpy(keybuf, key, KEYBUF_SIZE);
/* deal with case that we are doing a password check for a
conventially encrypted password: the salt will be
SALT_SIZE+ESEGMENT_SIZE long. */
if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE))
keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */
keylen = strlen(keybuf);
if (!keylen) {
n_seg = 1;
} else {
/* work out how many segments */
n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE);
}
if (n_seg > MAX_PASS_LEN)
n_seg = MAX_PASS_LEN; /* truncate at max length */
/* set up some pointers */
cipher_ptr = dec_c2_cryptbuf;
plaintext_ptr = keybuf;
/* do the first block with supplied salt */
#ifdef HAVE_CRYPT_R
tmp_ptr = crypt_r(plaintext_ptr, salt, cdata); /* libc crypt_r() */
#else
tmp_ptr = crypt(plaintext_ptr, salt); /* libc crypt() */
#endif
if (tmp_ptr == NULL) {
pam_overwrite_array(keybuf);
free(dec_c2_cryptbuf);
#ifdef HAVE_CRYPT_R
pam_overwrite_object(cdata);
free(cdata);
#endif
return NULL;
}
/* and place in the static area */
strncpy(cipher_ptr, tmp_ptr, 13);
pam_overwrite_string(tmp_ptr);
cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */
/* change the salt (1st 2 chars of previous block) - this was found
by dowsing */
salt_ptr = cipher_ptr - ESEGMENT_SIZE;
/* so far this is identical to "return crypt(key, salt);", if
there is more than one block encrypt them... */
if (n_seg > 1) {
for (j = 2; j <= n_seg; j++) {
#ifdef HAVE_CRYPT_R
tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata);
#else
tmp_ptr = crypt(plaintext_ptr, salt_ptr);
#endif
if (tmp_ptr == NULL) {
pam_overwrite_array(keybuf);
pam_overwrite_string(dec_c2_cryptbuf);
free(dec_c2_cryptbuf);
#ifdef HAVE_CRYPT_R
pam_overwrite_object(cdata);
free(cdata);
#endif
return NULL;
}
/* skip the salt for seg!=0 */
strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE);
pam_overwrite_string(tmp_ptr);
cipher_ptr += ESEGMENT_SIZE;
plaintext_ptr += SEGMENT_SIZE;
salt_ptr = cipher_ptr - ESEGMENT_SIZE;
}
}
D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
pam_overwrite_array(keybuf);
#ifdef HAVE_CRYPT_R
pam_overwrite_object(cdata);
free(cdata);
#endif
/* this is the <NUL> terminated encrypted password */
return dec_c2_cryptbuf;
}