]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/bigcrypt.c
Relevant BUGIDs:
[linux-pam] / 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 suceeding 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 #ifdef HAVE_LIBXCRYPT
33 #include <xcrypt.h>
34 #elif defined(HAVE_CRYPT_H)
35 #include <crypt.h>
36 #endif
37
38 #include "bigcrypt.h"
39
40 /*
41  * Max cleartext password length in segments of 8 characters this
42  * function can deal with (16 segments of 8 chars= max 128 character
43  * password).
44  */
45
46 #define MAX_PASS_LEN       16
47 #define SEGMENT_SIZE       8
48 #define SALT_SIZE          2
49 #define KEYBUF_SIZE        ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE)
50 #define ESEGMENT_SIZE      11
51 #define CBUF_SIZE          ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1)
52
53 char *bigcrypt(const char *key, const char *salt)
54 {
55         char *dec_c2_cryptbuf;
56 #ifdef HAVE_CRYPT_R
57         struct crypt_data *cdata;
58 #endif
59         unsigned long int keylen, n_seg, j;
60         char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
61         char keybuf[KEYBUF_SIZE + 1];
62
63         D(("called with key='%s', salt='%s'.", key, salt));
64
65         /* reset arrays */
66         dec_c2_cryptbuf = malloc(CBUF_SIZE);
67         if (!dec_c2_cryptbuf) {
68                 return NULL;
69         }
70 #ifdef HAVE_CRYPT_R
71         cdata = malloc(sizeof(*cdata));
72         if(!cdata) {
73                 free(dec_c2_cryptbuf);
74                 return NULL;
75         }
76         cdata->initialized = 0;
77 #endif
78         memset(keybuf, 0, KEYBUF_SIZE + 1);
79         memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
80
81         /* fill KEYBUF_SIZE with key */
82         strncpy(keybuf, key, KEYBUF_SIZE);
83
84         /* deal with case that we are doing a password check for a
85            conventially encrypted password: the salt will be
86            SALT_SIZE+ESEGMENT_SIZE long. */
87         if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE))
88                 keybuf[SEGMENT_SIZE] = '\0';    /* terminate password early(?) */
89
90         keylen = strlen(keybuf);
91
92         if (!keylen) {
93                 n_seg = 1;
94         } else {
95                 /* work out how many segments */
96                 n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE);
97         }
98
99         if (n_seg > MAX_PASS_LEN)
100                 n_seg = MAX_PASS_LEN;   /* truncate at max length */
101
102         /* set up some pointers */
103         cipher_ptr = dec_c2_cryptbuf;
104         plaintext_ptr = keybuf;
105
106         /* do the first block with supplied salt */
107 #ifdef HAVE_CRYPT_R
108         tmp_ptr = crypt_r(plaintext_ptr, salt, cdata);  /* libc crypt_r() */
109 #else
110         tmp_ptr = crypt(plaintext_ptr, salt);   /* libc crypt() */
111 #endif
112         /* and place in the static area */
113         strncpy(cipher_ptr, tmp_ptr, 13);
114         cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
115         plaintext_ptr += SEGMENT_SIZE;  /* first block of SEGMENT_SIZE */
116
117         /* change the salt (1st 2 chars of previous block) - this was found
118            by dowsing */
119
120         salt_ptr = cipher_ptr - ESEGMENT_SIZE;
121
122         /* so far this is identical to "return crypt(key, salt);", if
123            there is more than one block encrypt them... */
124
125         if (n_seg > 1) {
126                 for (j = 2; j <= n_seg; j++) {
127
128 #ifdef HAVE_CRYPT_R
129                         tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata);
130 #else
131                         tmp_ptr = crypt(plaintext_ptr, salt_ptr);
132 #endif
133
134                         /* skip the salt for seg!=0 */
135                         strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE);
136
137                         cipher_ptr += ESEGMENT_SIZE;
138                         plaintext_ptr += SEGMENT_SIZE;
139                         salt_ptr = cipher_ptr - ESEGMENT_SIZE;
140                 }
141         }
142         D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
143
144 #ifdef HAVE_CRYPT_R
145         free(cdata);
146 #endif
147
148         /* this is the <NUL> terminated encrypted password */
149         return dec_c2_cryptbuf;
150 }