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 }