xref: /illumos-gate/usr/src/lib/libsmbfs/smb/crypt.c (revision 85e6b674)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 /*
29  * Crypto support, using libpkcs11
30  *
31  * Some code copied from the server: libsmb smb_crypt.c
32  * with minor changes, i.e. errno.h return values.
33  * XXX: Later, make the server use these.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/md4.h>
38 
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <string.h>
42 
43 #include <security/cryptoki.h>
44 #include <security/pkcs11.h>
45 #include <cryptoutil.h>
46 
47 #include "smb_crypt.h"
48 
49 static void
50 smb_initlmkey(uchar_t *keyout, const uchar_t *keyin);
51 
52 /*
53  * Like libsmb smb_auth_DES,
54  * but use uchar_t, return errno.
55  */
56 int
smb_encrypt_DES(uchar_t * Result,int ResultLen,const uchar_t * Key,int KeyLen,const uchar_t * Data,int DataLen)57 smb_encrypt_DES(uchar_t *Result, int ResultLen,
58     const uchar_t *Key, int KeyLen,
59     const uchar_t *Data, int DataLen)
60 {
61 	CK_RV rv;
62 	CK_MECHANISM mechanism;
63 	CK_OBJECT_HANDLE hKey;
64 	CK_SESSION_HANDLE hSession;
65 	CK_ULONG ciphertext_len;
66 	uchar_t des_key[8];
67 	int error = 0;
68 	int K, D;
69 	int k, d;
70 
71 	/*
72 	 * Calculate proper number of iterations.
73 	 * Known call cases include:
74 	 *   ResultLen=16, KeyLen=14, DataLen=8
75 	 *   ResultLen=24, KeyLen=21, DataLen=8
76 	 *   ResultLen=16, KeyLen=14, DataLen=16
77 	 */
78 	K = KeyLen / 7;
79 	D = DataLen / 8;
80 	if ((KeyLen % 7) || (DataLen % 8))
81 		return (EINVAL);
82 	if (K == 0 || D == 0)
83 		return (EINVAL);
84 	if (ResultLen < (K * 8))
85 		return (EINVAL);
86 
87 	/*
88 	 * Use SUNW convenience function to initialize the cryptoki
89 	 * library, and open a session with a slot that supports
90 	 * the mechanism we plan on using.
91 	 */
92 	mechanism.mechanism = CKM_DES_ECB;
93 	mechanism.pParameter = NULL;
94 	mechanism.ulParameterLen = 0;
95 	rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
96 	if (rv != CKR_OK) {
97 		return (ENOTSUP);
98 	}
99 
100 	for (d = k = 0; k < K; k++, d++) {
101 		/* Cycle the input again, as necessary. */
102 		if (d == D)
103 			d = 0;
104 		smb_initlmkey(des_key, &Key[k * 7]);
105 		rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism,
106 		    des_key, 8, &hKey);
107 		if (rv != CKR_OK) {
108 			error = EIO;
109 			goto exit_session;
110 		}
111 		/* Initialize the encryption operation in the session */
112 		rv = C_EncryptInit(hSession, &mechanism, hKey);
113 		if (rv != CKR_OK) {
114 			error = EIO;
115 			goto exit_encrypt;
116 		}
117 		ciphertext_len = 8;
118 
119 		/* Read in the data and encrypt this portion */
120 		rv = C_EncryptUpdate(hSession,
121 		    (CK_BYTE_PTR)Data + (d * 8), 8,
122 		    (CK_BYTE_PTR)Result + (k * 8),
123 		    &ciphertext_len);
124 		if (rv != CKR_OK) {
125 			error = EIO;
126 			goto exit_encrypt;
127 		}
128 
129 		(void) C_DestroyObject(hSession, hKey);
130 	}
131 	goto exit_session;
132 
133 exit_encrypt:
134 	(void) C_DestroyObject(hSession, hKey);
135 exit_session:
136 	(void) C_CloseSession(hSession);
137 
138 	return (error);
139 }
140 
141 /*
142  * See "Netlogon Credential Computation" section of MS-NRPC document.
143  * Same as in libsmb, but output arg first.
144  */
145 static void
smb_initlmkey(uchar_t * keyout,const uchar_t * keyin)146 smb_initlmkey(uchar_t *keyout, const uchar_t *keyin)
147 {
148 	int i;
149 
150 	keyout[0] = keyin[0] >> 0x01;
151 	keyout[1] = ((keyin[0] & 0x01) << 6) | (keyin[1] >> 2);
152 	keyout[2] = ((keyin[1] & 0x03) << 5) | (keyin[2] >> 3);
153 	keyout[3] = ((keyin[2] & 0x07) << 4) | (keyin[3] >> 4);
154 	keyout[4] = ((keyin[3] & 0x0f) << 3) | (keyin[4] >> 5);
155 	keyout[5] = ((keyin[4] & 0x1f) << 2) | (keyin[5] >> 6);
156 	keyout[6] = ((keyin[5] & 0x3f) << 1) | (keyin[6] >> 7);
157 	keyout[7] = keyin[6] & 0x7f;
158 
159 	for (i = 0; i < 8; i++)
160 		keyout[i] = (keyout[i] << 1) & 0xfe;
161 }
162 
163 /*
164  * CKM_RC4
165  */
166 int
smb_encrypt_RC4(uchar_t * Result,int ResultLen,const uchar_t * Key,int KeyLen,const uchar_t * Data,int DataLen)167 smb_encrypt_RC4(uchar_t *Result, int ResultLen,
168 	const uchar_t *Key, int KeyLen,
169 	const uchar_t *Data, int DataLen)
170 {
171 	CK_RV rv;
172 	CK_MECHANISM mechanism;
173 	CK_OBJECT_HANDLE hKey;
174 	CK_SESSION_HANDLE hSession;
175 	CK_ULONG ciphertext_len;
176 	int error = EIO;
177 
178 	/*
179 	 * Use SUNW convenience function to initialize the cryptoki
180 	 * library, and open a session with a slot that supports
181 	 * the mechanism we plan on using.
182 	 */
183 	mechanism.mechanism = CKM_RC4;
184 	mechanism.pParameter = NULL;
185 	mechanism.ulParameterLen = 0;
186 	rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
187 	if (rv != CKR_OK) {
188 		return (ENOTSUP);
189 	}
190 
191 	rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism,
192 	    Key, KeyLen, &hKey);
193 	if (rv != CKR_OK)
194 		goto exit_session;
195 
196 	/* Initialize the encryption operation in the session */
197 	rv = C_EncryptInit(hSession, &mechanism, hKey);
198 	if (rv != CKR_OK)
199 		goto exit_encrypt;
200 
201 	ciphertext_len = ResultLen;
202 	rv = C_EncryptUpdate(hSession,
203 	    (CK_BYTE_PTR)Data, DataLen,
204 	    (CK_BYTE_PTR)Result, &ciphertext_len);
205 	if (rv == CKR_OK)
206 		error = 0;
207 
208 exit_encrypt:
209 	(void) C_DestroyObject(hSession, hKey);
210 exit_session:
211 	(void) C_CloseSession(hSession);
212 
213 	return (error);
214 }
215 
216 /*
217  * Get some random bytes from /dev/urandom
218  *
219  * There may be a preferred way to call this via libpkcs11
220  * XXX: (see: C_GenerateRandom, etc. -- later...)
221  * Just read from /dev/urandom for now.
222  */
223 int
smb_get_urandom(void * data,size_t dlen)224 smb_get_urandom(void *data, size_t dlen)
225 {
226 	int fd, rlen;
227 
228 	fd = open("/dev/urandom", O_RDONLY);
229 	if (fd < 0)
230 		return (errno);
231 
232 	rlen = read(fd, data, dlen);
233 	close(fd);
234 
235 	if (rlen < 0)
236 		return (errno);
237 	if (rlen < dlen)
238 		return (EIO);
239 	return (0);
240 }
241