(root)/
Linux-PAM-1.5.3/
modules/
pam_unix/
bigcrypt.c
       1  /*
       2   * This function implements the "bigcrypt" algorithm specifically for
       3   * Linux-PAM.
       4   *
       5   * This algorithm is algorithm 0 (default) shipped with the C2 secure
       6   * implementation of Digital UNIX.
       7   *
       8   * Disclaimer: This work is not based on the source code to Digital
       9   * UNIX, nor am I connected to Digital Equipment Corp, in any way
      10   * other than as a customer. This code is based on published
      11   * interfaces and reasonable guesswork.
      12   *
      13   * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8
      14   * characters or less. Each block is encrypted using the standard UNIX
      15   * libc crypt function. The result of the encryption for one block
      16   * provides the salt for the succeeding block.
      17   *
      18   * Restrictions: The buffer used to hold the encrypted result is
      19   * statically allocated. (see MAX_PASS_LEN below).  This is necessary,
      20   * as the returned pointer points to "static data that are overwritten
      21   * by each call", (XPG3: XSI System Interface + Headers pg 109), and
      22   * this is a drop in replacement for crypt();
      23   *
      24   * Andy Phillips <atp@mssl.ucl.ac.uk>
      25   */
      26  
      27  #include "config.h"
      28  
      29  #include <string.h>
      30  #include <stdlib.h>
      31  #include <security/_pam_macros.h>
      32  #include "pam_inline.h"
      33  #ifdef HAVE_CRYPT_H
      34  #include <crypt.h>
      35  #endif
      36  
      37  #include "bigcrypt.h"
      38  
      39  /*
      40   * Max cleartext password length in segments of 8 characters this
      41   * function can deal with (16 segments of 8 chars= max 128 character
      42   * password).
      43   */
      44  
      45  #define MAX_PASS_LEN       16
      46  #define SEGMENT_SIZE       8
      47  #define SALT_SIZE          2
      48  #define KEYBUF_SIZE        ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
      49  #define ESEGMENT_SIZE      11
      50  #define CBUF_SIZE          ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
      51  
      52  char *bigcrypt(const char *key, const char *salt)
      53  {
      54  	char *dec_c2_cryptbuf;
      55  #ifdef HAVE_CRYPT_R
      56  	struct crypt_data *cdata;
      57  #endif
      58  	unsigned long int keylen, n_seg, j;
      59  	char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
      60  	char keybuf[KEYBUF_SIZE + 1] = {};
      61  
      62  	D(("called with key='%s', salt='%s'.", key, salt));
      63  
      64  	/* reset arrays */
      65  	dec_c2_cryptbuf = calloc(1, CBUF_SIZE);
      66  	if (!dec_c2_cryptbuf) {
      67  		return NULL;
      68  	}
      69  #ifdef HAVE_CRYPT_R
      70  	cdata = malloc(sizeof(*cdata));
      71  	if(!cdata) {
      72  		free(dec_c2_cryptbuf);
      73  		return NULL;
      74  	}
      75  	cdata->initialized = 0;
      76  #endif
      77  
      78  	/* fill KEYBUF_SIZE with key */
      79  	strncpy(keybuf, key, KEYBUF_SIZE);
      80  
      81  	/* deal with case that we are doing a password check for a
      82  	   conventially encrypted password: the salt will be
      83  	   SALT_SIZE+ESEGMENT_SIZE long. */
      84  	if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE))
      85  		keybuf[SEGMENT_SIZE] = '\0';	/* terminate password early(?) */
      86  
      87  	keylen = strlen(keybuf);
      88  
      89  	if (!keylen) {
      90  		n_seg = 1;
      91  	} else {
      92  		/* work out how many segments */
      93  		n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE);
      94  	}
      95  
      96  	if (n_seg > MAX_PASS_LEN)
      97  		n_seg = MAX_PASS_LEN;	/* truncate at max length */
      98  
      99  	/* set up some pointers */
     100  	cipher_ptr = dec_c2_cryptbuf;
     101  	plaintext_ptr = keybuf;
     102  
     103  	/* do the first block with supplied salt */
     104  #ifdef HAVE_CRYPT_R
     105  	tmp_ptr = crypt_r(plaintext_ptr, salt, cdata);	/* libc crypt_r() */
     106  #else
     107  	tmp_ptr = crypt(plaintext_ptr, salt);	/* libc crypt() */
     108  #endif
     109  	if (tmp_ptr == NULL) {
     110  		free(dec_c2_cryptbuf);
     111  #ifdef HAVE_CRYPT_R
     112  		free(cdata);
     113  #endif
     114  		return NULL;
     115  	}
     116  	/* and place in the static area */
     117  	strncpy(cipher_ptr, tmp_ptr, 13);
     118  	pam_overwrite_string(tmp_ptr);
     119  	cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
     120  	plaintext_ptr += SEGMENT_SIZE;	/* first block of SEGMENT_SIZE */
     121  
     122  	/* change the salt (1st 2 chars of previous block) - this was found
     123  	   by dowsing */
     124  
     125  	salt_ptr = cipher_ptr - ESEGMENT_SIZE;
     126  
     127  	/* so far this is identical to "return crypt(key, salt);", if
     128  	   there is more than one block encrypt them... */
     129  
     130  	if (n_seg > 1) {
     131  		for (j = 2; j <= n_seg; j++) {
     132  
     133  #ifdef HAVE_CRYPT_R
     134  			tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata);
     135  #else
     136  			tmp_ptr = crypt(plaintext_ptr, salt_ptr);
     137  #endif
     138  			if (tmp_ptr == NULL) {
     139  				pam_overwrite_string(dec_c2_cryptbuf);
     140  				free(dec_c2_cryptbuf);
     141  #ifdef HAVE_CRYPT_R
     142  				pam_overwrite_object(cdata);
     143  				free(cdata);
     144  #endif
     145  				return NULL;
     146  			}
     147  
     148  			/* skip the salt for seg!=0 */
     149  			strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE);
     150  			pam_overwrite_string(tmp_ptr);
     151  
     152  			cipher_ptr += ESEGMENT_SIZE;
     153  			plaintext_ptr += SEGMENT_SIZE;
     154  			salt_ptr = cipher_ptr - ESEGMENT_SIZE;
     155  		}
     156  	}
     157  	D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
     158  
     159  #ifdef HAVE_CRYPT_R
     160  	pam_overwrite_object(cdata);
     161  	free(cdata);
     162  #endif
     163  
     164  	/* this is the <NUL> terminated encrypted password */
     165  	return dec_c2_cryptbuf;
     166  }