43ce9d54fdc1cfbd75ebaf3ef39aea9e3fff2f53
[samba.git] / libcli / auth / session.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    code to encrypt/decrypt data using the user session key
5
6    Copyright (C) Andrew Tridgell 2004
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/auth/libcli_auth.h"
24
25 /*
26   encrypt or decrypt a blob of data using the user session key
27   as used in lsa_SetSecret
28
29   before calling, the out blob must be initialised to be the same size
30   as the in blob
31 */
32 int sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key,
33                     enum samba_gnutls_direction encrypt)
34 {
35         int i, k, rc;
36
37         if (in->length % 8 != 0) {
38                 return GNUTLS_E_INVALID_REQUEST;
39         }
40
41         for (i=0,k=0;
42              i<in->length;
43              i += 8, k += 7) {
44                 uint8_t bin[8], bout[8], key[7];
45
46                 memcpy(bin,  &in->data[i], 8);
47
48                 if (k + 7 > session_key->length) {
49                         k = (session_key->length - k);
50                 }
51                 memcpy(key, &session_key->data[k], 7);
52
53                 rc = des_crypt56_gnutls(bout, bin, key, encrypt);
54                 if (rc != 0) {
55                         return rc;
56                 }
57
58                 memcpy(&out->data[i], bout, 8);
59         }
60         return 0;
61 }
62
63
64 /*
65   a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention
66
67   note that we round the length to a multiple of 8. This seems to be needed for 
68   compatibility with windows
69
70   caller should free using data_blob_free()
71 */
72 DATA_BLOB sess_encrypt_string(const char *str, const DATA_BLOB *session_key)
73 {
74         DATA_BLOB ret, src;
75         int slen = strlen(str);
76         int dlen = (slen+7) & ~7;
77         int rc;
78
79         src = data_blob(NULL, 8+dlen);
80         if (!src.data) {
81                 return data_blob(NULL, 0);
82         }
83
84         ret = data_blob(NULL, 8+dlen);
85         if (!ret.data) {
86                 data_blob_free(&src);
87                 return data_blob(NULL, 0);
88         }
89
90         SIVAL(src.data, 0, slen);
91         SIVAL(src.data, 4, 1);
92         memset(src.data+8, 0,   dlen);
93         memcpy(src.data+8, str, slen);
94
95         rc = sess_crypt_blob(&ret, &src, session_key, SAMBA_GNUTLS_ENCRYPT);
96         
97         data_blob_free(&src);
98         if (rc != 0) {
99                 data_blob_free(&ret);
100                 return data_blob(NULL, 0);
101         }
102
103         return ret;
104 }
105
106 /*
107   a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention
108
109   caller should free the returned string
110 */
111 char *sess_decrypt_string(TALLOC_CTX *mem_ctx, 
112                           DATA_BLOB *blob, const DATA_BLOB *session_key)
113 {
114         DATA_BLOB out;
115         int rc, slen;
116         char *ret;
117
118         if (blob->length < 8) {
119                 return NULL;
120         }
121         
122         out = data_blob_talloc(mem_ctx, NULL, blob->length);
123         if (!out.data) {
124                 return NULL;
125         }
126
127         rc = sess_crypt_blob(&out, blob, session_key, SAMBA_GNUTLS_DECRYPT);
128         if (rc != 0) {
129                 data_blob_free(&out);
130                 return NULL;
131         }
132
133         if (IVAL(out.data, 4) != 1) {
134                 DEBUG(0,("Unexpected revision number %d in session crypted string\n",
135                          IVAL(out.data, 4)));
136                 data_blob_free(&out);
137                 return NULL;
138         }
139
140         slen = IVAL(out.data, 0);
141         if (slen > blob->length - 8) {
142                 DEBUG(0,("Invalid crypt length %d\n", slen));
143                 data_blob_free(&out);
144                 return NULL;
145         }
146
147         ret = talloc_strndup(mem_ctx, (const char *)(out.data+8), slen);
148
149         data_blob_free(&out);
150
151         DEBUG(0,("decrypted string '%s' of length %d\n", ret, slen));
152
153         return ret;
154 }
155
156 /*
157   a convenient wrapper around sess_crypt_blob() for DATA_BLOBs, using the LSA convention
158
159   note that we round the length to a multiple of 8. This seems to be needed for 
160   compatibility with windows
161
162   caller should free using data_blob_free()
163 */
164 DATA_BLOB sess_encrypt_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob_in, const DATA_BLOB *session_key)
165 {
166         DATA_BLOB ret, src;
167         int dlen = (blob_in->length+7) & ~7;
168         int rc;
169
170         src = data_blob_talloc(mem_ctx, NULL, 8+dlen);
171         if (!src.data) {
172                 return data_blob(NULL, 0);
173         }
174
175         ret = data_blob_talloc(mem_ctx, NULL, 8+dlen);
176         if (!ret.data) {
177                 data_blob_free(&src);
178                 return data_blob(NULL, 0);
179         }
180
181         SIVAL(src.data, 0, blob_in->length);
182         SIVAL(src.data, 4, 1);
183         memset(src.data+8, 0, dlen);
184         memcpy(src.data+8, blob_in->data, blob_in->length);
185
186         rc = sess_crypt_blob(&ret, &src, session_key, SAMBA_GNUTLS_ENCRYPT);
187         
188         data_blob_free(&src);
189         if (rc != 0) {
190                 data_blob_free(&ret);
191                 return data_blob(NULL, 0);
192         }
193
194         return ret;
195 }
196
197 /*
198   Decrypt a DATA_BLOB using the LSA convention
199 */
200 NTSTATUS sess_decrypt_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const DATA_BLOB *session_key, 
201                            DATA_BLOB *ret)
202 {
203         DATA_BLOB out;
204         int rc, slen;
205
206         if (blob->length < 8) {
207                 DEBUG(0, ("Unexpected length %d in session crypted secret (BLOB)\n",
208                           (int)blob->length));
209                 return NT_STATUS_INVALID_PARAMETER;
210         }
211         
212         out = data_blob_talloc(mem_ctx, NULL, blob->length);
213         if (!out.data) {
214                 return NT_STATUS_NO_MEMORY;
215         }
216
217         rc = sess_crypt_blob(&out, blob, session_key, SAMBA_GNUTLS_DECRYPT);
218         if (rc != 0) {
219                 data_blob_free(&out);
220                 return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
221         }
222
223         if (IVAL(out.data, 4) != 1) {
224                 DEBUG(2,("Unexpected revision number %d in session crypted secret (BLOB)\n",
225                          IVAL(out.data, 4)));
226                 return NT_STATUS_UNKNOWN_REVISION;
227         }
228                 
229         slen = IVAL(out.data, 0);
230         if (slen > blob->length - 8) {
231                 DEBUG(0,("Invalid crypt length %d in session crypted secret (BLOB)\n", slen));
232                 return NT_STATUS_WRONG_PASSWORD;
233         }
234
235         *ret = data_blob_talloc(mem_ctx, out.data+8, slen);
236         if (slen && !ret->data) {
237                 return NT_STATUS_NO_MEMORY;
238         }
239
240         data_blob_free(&out);
241
242         return NT_STATUS_OK;
243 }