r21923: Add in the gss decrypt.
[samba.git] / source / libsmb / smb_seal.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB Transport encryption (sealing) code.
4    Copyright (C) Jeremy Allison 2007.
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /******************************************************************************
24  Generic code for client and server.
25  Is encryption turned on ?
26 ******************************************************************************/
27
28 BOOL common_encryption_on(struct smb_trans_enc_state *es)
29 {
30         return ((es != NULL) && es->enc_on);
31 }
32
33 /******************************************************************************
34  Generic code for client and server.
35  NTLM decrypt an incoming buffer.
36 ******************************************************************************/
37
38 NTSTATUS common_ntlm_decrypt_buffer(NTLMSSP_STATE *ntlmssp_state, char *buf)
39 {
40         NTSTATUS status;
41         size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
42         DATA_BLOB sig;
43
44         if (buf_len < 8 + NTLMSSP_SIG_SIZE) {
45                 return NT_STATUS_BUFFER_TOO_SMALL;
46         }
47
48         /* Adjust for the signature. */
49         buf_len -= NTLMSSP_SIG_SIZE;
50
51         /* Save off the signature. */
52         sig = data_blob(buf+buf_len, NTLMSSP_SIG_SIZE);
53
54         status = ntlmssp_unseal_packet(ntlmssp_state,
55                 (unsigned char *)buf + 8, /* 4 byte len + 0xFF 'S' 'M' 'B' */
56                 buf_len - 8,
57                 (unsigned char *)buf + 8,
58                 buf_len - 8,
59                 &sig);
60
61         if (!NT_STATUS_IS_OK(status)) {
62                 data_blob_free(&sig);
63                 return status;
64         }
65
66         /* Reset the length. */
67         smb_setlen(buf, smb_len(buf) - NTLMSSP_SIG_SIZE);
68         return NT_STATUS_OK;
69 }
70
71 /******************************************************************************
72  Generic code for client and server.
73  NTLM encrypt an outgoing buffer. Return the encrypted pointer in ppbuf_out.
74 ******************************************************************************/
75
76 NTSTATUS common_ntlm_encrypt_buffer(NTLMSSP_STATE *ntlmssp_state, char *buf, char **ppbuf_out)
77 {
78         NTSTATUS status;
79         char *buf_out;
80         size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
81         DATA_BLOB sig;
82
83         *ppbuf_out = NULL;
84
85         if (buf_len < 8) {
86                 return NT_STATUS_BUFFER_TOO_SMALL;
87         }
88
89         /* 
90          * We know smb_len can't return a value > 128k, so no int overflow
91          * check needed.
92          */
93
94         /* Copy the original buffer. */
95
96         buf_out = SMB_XMALLOC_ARRAY(char, buf_len + NTLMSSP_SIG_SIZE);
97         memcpy(buf_out, buf, buf_len);
98         /* Last 16 bytes undefined here... */
99
100         smb_setlen(buf_out, smb_len(buf) + NTLMSSP_SIG_SIZE);
101
102         sig = data_blob(NULL, NTLMSSP_SIG_SIZE);
103
104         status = ntlmssp_seal_packet(ntlmssp_state,
105                 (unsigned char *)buf_out + 8, /* 4 byte len + 0xFF 'S' 'M' 'B' */
106                 buf_len - 8,
107                 (unsigned char *)buf_out + 8,
108                 buf_len - 8,
109                 &sig);
110
111         if (!NT_STATUS_IS_OK(status)) {
112                 data_blob_free(&sig);
113                 SAFE_FREE(buf_out);
114                 return status;
115         }
116
117         memcpy(buf_out+buf_len, sig.data, NTLMSSP_SIG_SIZE);
118         *ppbuf_out = buf_out;
119         return NT_STATUS_OK;
120 }
121
122 /******************************************************************************
123  Generic code for client and server.
124  gss-api decrypt an incoming buffer. We insist that the size of the
125  unwrapped buffer must be smaller or identical to the incoming buffer.
126 ******************************************************************************/
127
128 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
129  NTSTATUS common_gss_decrypt_buffer(gss_ctx_id_t context_handle, char *buf)
130 {
131         OM_uint32 ret = 0;
132         OM_uint32 minor = 0;
133         int flags_got = 0;
134         gss_buffer_desc in_buf, out_buf;
135         size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
136
137         if (buf_len < 8) {
138                 return NT_STATUS_BUFFER_TOO_SMALL;
139         }
140
141         in_buf.value = buf + 8;
142         in_buf.length = buf_len - 8;
143
144         ret = gss_unwrap(&minor,
145                         context_handle,
146                         &in_buf,
147                         &out_buf,
148                         &flags_got,             /* did we get sign+seal ? */
149                         (gss_qop_t *) NULL);    
150
151         if (ret != GSS_S_COMPLETE) {
152                 ADS_STATUS adss = ADS_ERROR_GSS(ret, minor);
153                 DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap failed. Error %s\n",
154                         ads_errstr(adss) ));
155                 /* Um - no mapping for gss-errs to NTSTATUS yet. */
156                 return ads_ntstatus(adss);
157         }
158
159         if (out_buf.length > in_buf.length) {
160                 DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap size (%u) too large (%u) !\n",
161                         (unsigned int)out_buf.length,
162                         (unsigned int)in_buf.length ));
163                 gss_release_buffer(&minor, &out_buf);
164                 return NT_STATUS_INVALID_PARAMETER;
165         }
166
167         memcpy(buf + 8, out_buf.value, out_buf.length);
168         smb_setlen(buf, out_buf.length + 4);
169
170         gss_release_buffer(&minor, &out_buf);
171         return NT_STATUS_OK;
172 }
173 #endif
174
175 /******************************************************************************
176  Generic code for client and server.
177  gss-api encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out.
178 ******************************************************************************/
179
180 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
181  NTSTATUS common_gss_encrypt_buffer(gss_ctx_id_t context_handle, char *buf, char **ppbuf_out)
182 {
183         OM_uint32 ret = 0;
184         OM_uint32 minor = 0;
185         int flags_got = 0;
186         gss_buffer_desc in_buf, out_buf;
187         size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
188
189         *ppbuf_out = NULL;
190
191         if (buf_len < 8) {
192                 return NT_STATUS_BUFFER_TOO_SMALL;
193         }
194
195         in_buf.value = buf + 8;
196         in_buf.length = buf_len - 8;
197
198         ret = gss_wrap(&minor,
199                         context_handle,
200                         True,                   /* we want sign+seal. */
201                         GSS_C_QOP_DEFAULT,
202                         &in_buf,
203                         &flags_got,             /* did we get sign+seal ? */
204                         &out_buf);
205
206         if (ret != GSS_S_COMPLETE) {
207                 ADS_STATUS adss = ADS_ERROR_GSS(ret, minor);
208                 DEBUG(0,("common_gss_encrypt_buffer: gss_wrap failed. Error %s\n",
209                         ads_errstr(adss) ));
210                 /* Um - no mapping for gss-errs to NTSTATUS yet. */
211                 return ads_ntstatus(adss);
212         }
213
214         if (!flags_got) {
215                 /* Sign+seal not supported. */
216                 gss_release_buffer(&minor, &out_buf);
217                 return NT_STATUS_NOT_SUPPORTED;
218         }
219
220         /* Ya see - this is why I *hate* gss-api. I don't 
221          * want to have to malloc another buffer of the
222          * same size + 8 bytes just to get a continuous
223          * header + buffer, but gss won't let me pass in
224          * a pre-allocated buffer. Bastards (and you know
225          * who you are....). I might fix this by
226          * going to "encrypt_and_send" passing in a file
227          * descriptor and doing scatter-gather write with
228          * TCP cork on Linux. But I shouldn't have to
229          * bother :-*(. JRA.
230          */
231
232         *ppbuf_out = SMB_MALLOC(out_buf.length + 8); /* We know this can't wrap. */
233         if (!*ppbuf_out) {
234                 gss_release_buffer(&minor, &out_buf);
235                 return NT_STATUS_NO_MEMORY;
236         }
237
238         memcpy(*ppbuf_out+8, out_buf.value, out_buf.length);
239         smb_setlen(*ppbuf_out, out_buf.length + 4);
240
241         gss_release_buffer(&minor, &out_buf);
242         return NT_STATUS_OK;
243 }
244 #endif
245
246 /******************************************************************************
247  Generic code for client and server.
248  Encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out.
249 ******************************************************************************/
250
251 NTSTATUS common_encrypt_buffer(struct smb_trans_enc_state *es, char *buffer, char **buf_out)
252 {
253         if (!common_encryption_on(es)) {
254                 /* Not encrypting. */
255                 *buf_out = buffer;
256                 return NT_STATUS_OK;
257         }
258
259         /* Ignore session keepalives. */
260         if(CVAL(buffer,0) == SMBkeepalive) {
261                 *buf_out = buffer;
262                 return NT_STATUS_OK;
263         }
264
265         switch (es->smb_enc_type) {
266                 case SMB_TRANS_ENC_NTLM:
267                         return common_ntlm_encrypt_buffer(es->ntlmssp_state, buffer, buf_out);
268 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
269                 case SMB_TRANS_ENC_GSS:
270                         return common_gss_encrypt_buffer(es->context_handle, buffer, buf_out);
271 #endif
272                 default:
273                         return NT_STATUS_NOT_SUPPORTED;
274         }
275 }
276
277 /******************************************************************************
278  Generic code for client and server.
279  Decrypt an incoming SMB buffer. Replaces the data within it.
280  New data must be less than or equal to the current length.
281 ******************************************************************************/
282
283 NTSTATUS common_decrypt_buffer(struct smb_trans_enc_state *es, char *buf)
284 {
285         if (!common_encryption_on(es)) {
286                 /* Not decrypting. */
287                 return NT_STATUS_OK;
288         }
289
290         /* Ignore session keepalives. */
291         if(CVAL(buf,0) == SMBkeepalive) {
292                 return NT_STATUS_OK;
293         }
294
295         switch (es->smb_enc_type) {
296                 case SMB_TRANS_ENC_NTLM:
297                         return common_ntlm_decrypt_buffer(es->ntlmssp_state, buf);
298 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
299                 case SMB_TRANS_ENC_GSS:
300                         return common_gss_decrypt_buffer(es->context_handle, buf);
301 #endif
302                 default:
303                         return NT_STATUS_NOT_SUPPORTED;
304         }
305 }
306
307 /******************************************************************************
308  Shutdown an encryption state.
309 ******************************************************************************/
310
311 void common_free_encryption_state(struct smb_trans_enc_state **pp_es)
312 {
313         struct smb_trans_enc_state *es = *pp_es;
314
315         if (es == NULL) {
316                 return;
317         }
318
319         if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
320                 if (es->ntlmssp_state) {
321                         ntlmssp_end(&es->ntlmssp_state);
322                 }
323         }
324 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
325         if (es->smb_enc_type == SMB_TRANS_ENC_GSS) {
326                 /* Free the gss context handle. */
327         }
328 #endif
329         SAFE_FREE(es);
330         *pp_es = NULL;
331 }
332
333 /******************************************************************************
334  Free an encryption-allocated buffer.
335 ******************************************************************************/
336
337 void common_free_enc_buffer(struct smb_trans_enc_state *es, char *buf)
338 {
339         if (!common_encryption_on(es)) {
340                 return;
341         }
342
343         /* We know this is an smb buffer, and we
344          * didn't malloc, only copy, for a keepalive,
345          * so ignore session keepalives. */
346
347         if(CVAL(buf,0) == SMBkeepalive) {
348                 return;
349         }
350
351         if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
352                 SAFE_FREE(buf);
353                 return;
354         }
355
356 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
357         /* gss-api free buffer.... */
358 #endif
359 }
360
361 /******************************************************************************
362  Client side encryption.
363 ******************************************************************************/
364
365 /******************************************************************************
366  Is client encryption on ?
367 ******************************************************************************/
368
369 BOOL cli_encryption_on(struct cli_state *cli)
370 {
371         return common_encryption_on(cli->trans_enc_state);
372 }
373
374 /******************************************************************************
375  Shutdown a client encryption state.
376 ******************************************************************************/
377
378 void cli_free_encryption_context(struct cli_state *cli)
379 {
380         common_free_encryption_state(&cli->trans_enc_state);
381 }
382
383 /******************************************************************************
384  Free an encryption-allocated buffer.
385 ******************************************************************************/
386
387 void cli_free_enc_buffer(struct cli_state *cli, char *buf)
388 {
389         common_free_enc_buffer(cli->trans_enc_state, buf);
390 }
391
392 /******************************************************************************
393  Decrypt an incoming buffer.
394 ******************************************************************************/
395
396 NTSTATUS cli_decrypt_message(struct cli_state *cli)
397 {
398         return common_decrypt_buffer(cli->trans_enc_state, cli->inbuf);
399 }
400
401 /******************************************************************************
402  Encrypt an outgoing buffer. Return the encrypted pointer in buf_out.
403 ******************************************************************************/
404
405 NTSTATUS cli_encrypt_message(struct cli_state *cli, char **buf_out)
406 {
407         return common_encrypt_buffer(cli->trans_enc_state, cli->outbuf, buf_out);
408 }