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
49static void
50smb_initlmkey(uchar_t *keyout, const uchar_t *keyin);
51
52/*
53 * Like libsmb smb_auth_DES,
54 * but use uchar_t, return errno.
55 */
56int
57smb_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
133exit_encrypt:
134	(void) C_DestroyObject(hSession, hKey);
135exit_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 */
145static void
146smb_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 */
166int
167smb_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
208exit_encrypt:
209	(void) C_DestroyObject(hSession, hKey);
210exit_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 */
223int
224smb_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