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