s3: Convert cli_unix_extensions_version to async
[mat/samba.git] / source3 / libsmb / clifsinfo.c
1 /* 
2    Unix SMB/CIFS implementation.
3    FS info functions
4    Copyright (C) Stefan (metze) Metzmacher      2003
5    Copyright (C) Jeremy Allison 2007
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "../libcli/auth/spnego.h"
23
24 /****************************************************************************
25  Get UNIX extensions version info.
26 ****************************************************************************/
27
28 struct cli_unix_extensions_version_state {
29         uint16_t setup[1];
30         uint8_t param[2];
31         uint16_t major, minor;
32         uint32_t caplow, caphigh;
33 };
34
35 static void cli_unix_extensions_version_done(struct tevent_req *subreq);
36
37 struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx,
38                                                     struct tevent_context *ev,
39                                                     struct cli_state *cli)
40 {
41         struct tevent_req *req, *subreq;
42         struct cli_unix_extensions_version_state *state;
43
44         req = tevent_req_create(mem_ctx, &state,
45                                 struct cli_unix_extensions_version_state);
46         if (req == NULL) {
47                 return NULL;
48         }
49         SSVAL(state->setup, 0, TRANSACT2_QFSINFO);
50         SSVAL(state->param, 0, SMB_QUERY_CIFS_UNIX_INFO);
51
52         subreq = cli_trans_send(state, ev, cli, SMBtrans2,
53                                 NULL, 0, 0, 0,
54                                 state->setup, 1, 0,
55                                 state->param, 2, 0,
56                                 NULL, 0, 560);
57         if (tevent_req_nomem(subreq, req)) {
58                 return tevent_req_post(req, ev);
59         }
60         tevent_req_set_callback(subreq, cli_unix_extensions_version_done, req);
61         return req;
62 }
63
64 static void cli_unix_extensions_version_done(struct tevent_req *subreq)
65 {
66         struct tevent_req *req = tevent_req_callback_data(
67                 subreq, struct tevent_req);
68         struct cli_unix_extensions_version_state *state = tevent_req_data(
69                 req, struct cli_unix_extensions_version_state);
70         uint8_t *data;
71         uint32_t num_data;
72         NTSTATUS status;
73
74         status = cli_trans_recv(subreq, state, NULL, NULL, NULL, NULL,
75                                 &data, &num_data);
76         TALLOC_FREE(subreq);
77         if (!NT_STATUS_IS_OK(status)) {
78                 tevent_req_nterror(req, status);
79                 return;
80         }
81         if (num_data < 12) {
82                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
83                 return;
84         }
85
86         state->major = SVAL(data, 0);
87         state->minor = SVAL(data, 2);
88         state->caplow = IVAL(data, 4);
89         state->caphigh = IVAL(data, 8);
90         TALLOC_FREE(data);
91         tevent_req_done(req);
92 }
93
94 NTSTATUS cli_unix_extensions_version_recv(struct tevent_req *req,
95                                           uint16_t *pmajor, uint16_t *pminor,
96                                           uint32_t *pcaplow,
97                                           uint32_t *pcaphigh)
98 {
99         struct cli_unix_extensions_version_state *state = tevent_req_data(
100                 req, struct cli_unix_extensions_version_state);
101         NTSTATUS status;
102
103         if (tevent_req_is_nterror(req, &status)) {
104                 return status;
105         }
106         *pmajor = state->major;
107         *pminor = state->minor;
108         *pcaplow = state->caplow;
109         *pcaphigh = state->caphigh;
110         return NT_STATUS_OK;
111 }
112
113 NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor,
114                                      uint16 *pminor, uint32 *pcaplow,
115                                      uint32 *pcaphigh)
116 {
117         TALLOC_CTX *frame = talloc_stackframe();
118         struct event_context *ev;
119         struct tevent_req *req;
120         NTSTATUS status = NT_STATUS_OK;
121
122         if (cli_has_async_calls(cli)) {
123                 /*
124                  * Can't use sync call while an async call is in flight
125                  */
126                 status = NT_STATUS_INVALID_PARAMETER;
127                 goto fail;
128         }
129
130         ev = event_context_init(frame);
131         if (ev == NULL) {
132                 status = NT_STATUS_NO_MEMORY;
133                 goto fail;
134         }
135
136         req = cli_unix_extensions_version_send(frame, ev, cli);
137         if (req == NULL) {
138                 status = NT_STATUS_NO_MEMORY;
139                 goto fail;
140         }
141
142         if (!tevent_req_poll(req, ev)) {
143                 status = map_nt_error_from_unix(errno);
144                 goto fail;
145         }
146
147         status = cli_unix_extensions_version_recv(req, pmajor, pminor, pcaplow,
148                                                   pcaphigh);
149         if (NT_STATUS_IS_OK(status)) {
150                 cli->posix_capabilities = *pcaplow;
151         }
152  fail:
153         TALLOC_FREE(frame);
154         if (!NT_STATUS_IS_OK(status)) {
155                 cli_set_error(cli, status);
156         }
157         return status;
158 }
159
160 /****************************************************************************
161  Set UNIX extensions capabilities.
162 ****************************************************************************/
163
164 bool cli_set_unix_extensions_capabilities(struct cli_state *cli, uint16 major, uint16 minor,
165                                         uint32 caplow, uint32 caphigh)
166 {
167         bool ret = False;
168         uint16 setup;
169         char param[4];
170         char data[12];
171         char *rparam=NULL, *rdata=NULL;
172         unsigned int rparam_count=0, rdata_count=0;
173
174         setup = TRANSACT2_SETFSINFO;
175
176         SSVAL(param,0,0);
177         SSVAL(param,2,SMB_SET_CIFS_UNIX_INFO);
178
179         SSVAL(data,0,major);
180         SSVAL(data,2,minor);
181         SIVAL(data,4,caplow);
182         SIVAL(data,8,caphigh);
183
184         if (!cli_send_trans(cli, SMBtrans2,
185                     NULL,
186                     0, 0,
187                     &setup, 1, 0,
188                     param, 4, 0,
189                     data, 12, 560)) {
190                 goto cleanup;
191         }
192
193         if (!cli_receive_trans(cli, SMBtrans2,
194                               &rparam, &rparam_count,
195                               &rdata, &rdata_count)) {
196                 goto cleanup;
197         }
198
199         if (cli_is_error(cli)) {
200                 ret = False;
201                 goto cleanup;
202         } else {
203                 ret = True;
204         }
205
206 cleanup:
207         SAFE_FREE(rparam);
208         SAFE_FREE(rdata);
209
210         return ret;
211 }
212
213 bool cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr)
214 {
215         bool ret = False;
216         uint16 setup;
217         char param[2];
218         char *rparam=NULL, *rdata=NULL;
219         unsigned int rparam_count=0, rdata_count=0;
220
221         if (!cli||!fs_attr)
222                 smb_panic("cli_get_fs_attr_info() called with NULL Pionter!");
223
224         setup = TRANSACT2_QFSINFO;
225
226         SSVAL(param,0,SMB_QUERY_FS_ATTRIBUTE_INFO);
227
228         if (!cli_send_trans(cli, SMBtrans2,
229                     NULL,
230                     0, 0,
231                     &setup, 1, 0,
232                     param, 2, 0,
233                     NULL, 0, 560)) {
234                 goto cleanup;
235         }
236
237         if (!cli_receive_trans(cli, SMBtrans2,
238                               &rparam, &rparam_count,
239                               &rdata, &rdata_count)) {
240                 goto cleanup;
241         }
242
243         if (cli_is_error(cli)) {
244                 ret = False;
245                 goto cleanup;
246         } else {
247                 ret = True;
248         }
249
250         if (rdata_count < 12) {
251                 goto cleanup;
252         }
253
254         *fs_attr = IVAL(rdata,0);
255
256         /* todo: but not yet needed
257          *       return the other stuff
258          */
259
260 cleanup:
261         SAFE_FREE(rparam);
262         SAFE_FREE(rdata);
263
264         return ret;
265 }
266
267 bool cli_get_fs_volume_info_old(struct cli_state *cli, fstring volume_name, uint32 *pserial_number)
268 {
269         bool ret = False;
270         uint16 setup;
271         char param[2];
272         char *rparam=NULL, *rdata=NULL;
273         unsigned int rparam_count=0, rdata_count=0;
274         unsigned char nlen;
275
276         setup = TRANSACT2_QFSINFO;
277
278         SSVAL(param,0,SMB_INFO_VOLUME);
279
280         if (!cli_send_trans(cli, SMBtrans2,
281                     NULL,
282                     0, 0,
283                     &setup, 1, 0,
284                     param, 2, 0,
285                     NULL, 0, 560)) {
286                 goto cleanup;
287         }
288
289         if (!cli_receive_trans(cli, SMBtrans2,
290                               &rparam, &rparam_count,
291                               &rdata, &rdata_count)) {
292                 goto cleanup;
293         }
294
295         if (cli_is_error(cli)) {
296                 ret = False;
297                 goto cleanup;
298         } else {
299                 ret = True;
300         }
301
302         if (rdata_count < 5) {
303                 goto cleanup;
304         }
305
306         if (pserial_number) {
307                 *pserial_number = IVAL(rdata,0);
308         }
309         nlen = CVAL(rdata,l2_vol_cch);
310         clistr_pull(cli->inbuf, volume_name, rdata + l2_vol_szVolLabel,
311                     sizeof(fstring), nlen, STR_NOALIGN);
312
313         /* todo: but not yet needed
314          *       return the other stuff
315          */
316
317 cleanup:
318         SAFE_FREE(rparam);
319         SAFE_FREE(rdata);
320
321         return ret;
322 }
323
324 bool cli_get_fs_volume_info(struct cli_state *cli, fstring volume_name, uint32 *pserial_number, time_t *pdate)
325 {
326         bool ret = False;
327         uint16 setup;
328         char param[2];
329         char *rparam=NULL, *rdata=NULL;
330         unsigned int rparam_count=0, rdata_count=0;
331         unsigned int nlen;
332
333         setup = TRANSACT2_QFSINFO;
334
335         SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
336
337         if (!cli_send_trans(cli, SMBtrans2,
338                     NULL,
339                     0, 0,
340                     &setup, 1, 0,
341                     param, 2, 0,
342                     NULL, 0, 560)) {
343                 goto cleanup;
344         }
345
346         if (!cli_receive_trans(cli, SMBtrans2,
347                               &rparam, &rparam_count,
348                               &rdata, &rdata_count)) {
349                 goto cleanup;
350         }
351
352         if (cli_is_error(cli)) {
353                 ret = False;
354                 goto cleanup;
355         } else {
356                 ret = True;
357         }
358
359         if (rdata_count < 19) {
360                 goto cleanup;
361         }
362
363         if (pdate) {
364                 struct timespec ts;
365                 ts = interpret_long_date(rdata);
366                 *pdate = ts.tv_sec;
367         }
368         if (pserial_number) {
369                 *pserial_number = IVAL(rdata,8);
370         }
371         nlen = IVAL(rdata,12);
372         clistr_pull(cli->inbuf, volume_name, rdata + 18, sizeof(fstring),
373                     nlen, STR_UNICODE);
374
375         /* todo: but not yet needed
376          *       return the other stuff
377          */
378
379 cleanup:
380         SAFE_FREE(rparam);
381         SAFE_FREE(rdata);
382
383         return ret;
384 }
385
386 bool cli_get_fs_full_size_info(struct cli_state *cli,
387                                uint64_t *total_allocation_units,
388                                uint64_t *caller_allocation_units,
389                                uint64_t *actual_allocation_units,
390                                uint64_t *sectors_per_allocation_unit,
391                                uint64_t *bytes_per_sector)
392 {
393         bool ret = False;
394         uint16 setup;
395         char param[2];
396         char *rparam=NULL, *rdata=NULL;
397         unsigned int rparam_count=0, rdata_count=0;
398
399         setup = TRANSACT2_QFSINFO;
400
401         SSVAL(param,0,SMB_FS_FULL_SIZE_INFORMATION);
402
403         if (!cli_send_trans(cli, SMBtrans2,
404                     NULL,
405                     0, 0,
406                     &setup, 1, 0,
407                     param, 2, 0,
408                     NULL, 0, 560)) {
409                 goto cleanup;
410         }
411
412         if (!cli_receive_trans(cli, SMBtrans2,
413                               &rparam, &rparam_count,
414                               &rdata, &rdata_count)) {
415                 goto cleanup;
416         }
417
418         if (cli_is_error(cli)) {
419                 ret = False;
420                 goto cleanup;
421         } else {
422                 ret = True;
423         }
424
425         if (rdata_count != 32) {
426                 goto cleanup;
427         }
428
429         if (total_allocation_units) {
430                 *total_allocation_units = BIG_UINT(rdata, 0);
431         }
432         if (caller_allocation_units) {
433                 *caller_allocation_units = BIG_UINT(rdata,8);
434         }
435         if (actual_allocation_units) {
436                 *actual_allocation_units = BIG_UINT(rdata,16);
437         }
438         if (sectors_per_allocation_unit) {
439                 *sectors_per_allocation_unit = IVAL(rdata,24);
440         }
441         if (bytes_per_sector) {
442                 *bytes_per_sector = IVAL(rdata,28);
443         }
444
445 cleanup:
446         SAFE_FREE(rparam);
447         SAFE_FREE(rdata);
448
449         return ret;
450 }
451
452 bool cli_get_posix_fs_info(struct cli_state *cli,
453                            uint32 *optimal_transfer_size,
454                            uint32 *block_size,
455                            uint64_t *total_blocks,
456                            uint64_t *blocks_available,
457                            uint64_t *user_blocks_available,
458                            uint64_t *total_file_nodes,
459                            uint64_t *free_file_nodes,
460                            uint64_t *fs_identifier)
461 {
462         bool ret = False;
463         uint16 setup;
464         char param[2];
465         char *rparam=NULL, *rdata=NULL;
466         unsigned int rparam_count=0, rdata_count=0;
467
468         setup = TRANSACT2_QFSINFO;
469
470         SSVAL(param,0,SMB_QUERY_POSIX_FS_INFO);
471
472         if (!cli_send_trans(cli, SMBtrans2,
473                     NULL,
474                     0, 0,
475                     &setup, 1, 0,
476                     param, 2, 0,
477                     NULL, 0, 560)) {
478                 goto cleanup;
479         }
480
481         if (!cli_receive_trans(cli, SMBtrans2,
482                               &rparam, &rparam_count,
483                               &rdata, &rdata_count)) {
484                 goto cleanup;
485         }
486
487         if (cli_is_error(cli)) {
488                 ret = False;
489                 goto cleanup;
490         } else {
491                 ret = True;
492         }
493
494         if (rdata_count != 56) {
495                 goto cleanup;
496         }
497
498         if (optimal_transfer_size) {
499                 *optimal_transfer_size = IVAL(rdata, 0);
500         }
501         if (block_size) {
502                 *block_size = IVAL(rdata,4);
503         }
504         if (total_blocks) {
505                 *total_blocks = BIG_UINT(rdata,8);
506         }
507         if (blocks_available) {
508                 *blocks_available = BIG_UINT(rdata,16);
509         }
510         if (user_blocks_available) {
511                 *user_blocks_available = BIG_UINT(rdata,24);
512         }
513         if (total_file_nodes) {
514                 *total_file_nodes = BIG_UINT(rdata,32);
515         }
516         if (free_file_nodes) {
517                 *free_file_nodes = BIG_UINT(rdata,40);
518         }
519         if (fs_identifier) {
520                 *fs_identifier = BIG_UINT(rdata,48);
521         }
522
523 cleanup:
524         SAFE_FREE(rparam);
525         SAFE_FREE(rdata);
526
527         return ret;
528 }
529
530
531 /******************************************************************************
532  Send/receive the request encryption blob.
533 ******************************************************************************/
534
535 static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
536 {
537         uint16 setup;
538         char param[4];
539         char *rparam=NULL, *rdata=NULL;
540         unsigned int rparam_count=0, rdata_count=0;
541         NTSTATUS status = NT_STATUS_OK;
542
543         setup = TRANSACT2_SETFSINFO;
544
545         SSVAL(param,0,0);
546         SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
547
548         if (!cli_send_trans(cli, SMBtrans2,
549                                 NULL,
550                                 0, 0,
551                                 &setup, 1, 0,
552                                 param, 4, 0,
553                                 (char *)in->data, in->length, CLI_BUFFER_SIZE)) {
554                 status = cli_nt_error(cli);
555                 goto out;
556         }
557
558         if (!cli_receive_trans(cli, SMBtrans2,
559                                 &rparam, &rparam_count,
560                                 &rdata, &rdata_count)) {
561                 status = cli_nt_error(cli);
562                 goto out;
563         }
564
565         if (cli_is_error(cli)) {
566                 status = cli_nt_error(cli);
567                 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
568                         goto out;
569                 }
570         }
571
572         *out = data_blob(rdata, rdata_count);
573         *param_out = data_blob(rparam, rparam_count);
574
575   out:
576
577         SAFE_FREE(rparam);
578         SAFE_FREE(rdata);
579         return status;
580 }
581
582 /******************************************************************************
583  Make a client state struct.
584 ******************************************************************************/
585
586 static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type smb_enc_type)
587 {
588         struct smb_trans_enc_state *es = NULL;
589         es = SMB_MALLOC_P(struct smb_trans_enc_state);
590         if (!es) {
591                 return NULL;
592         }
593         ZERO_STRUCTP(es);
594         es->smb_enc_type = smb_enc_type;
595
596 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
597         if (smb_enc_type == SMB_TRANS_ENC_GSS) {
598                 es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
599                 if (!es->s.gss_state) {
600                         SAFE_FREE(es);
601                         return NULL;
602                 }
603                 ZERO_STRUCTP(es->s.gss_state);
604         }
605 #endif
606         return es;
607 }
608
609 /******************************************************************************
610  Start a raw ntlmssp encryption.
611 ******************************************************************************/
612
613 NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli, 
614                                 const char *user,
615                                 const char *pass,
616                                 const char *domain)
617 {
618         DATA_BLOB blob_in = data_blob_null;
619         DATA_BLOB blob_out = data_blob_null;
620         DATA_BLOB param_out = data_blob_null;
621         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
622         struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_NTLM);
623
624         if (!es) {
625                 return NT_STATUS_NO_MEMORY;
626         }
627         status = ntlmssp_client_start(&es->s.ntlmssp_state);
628         if (!NT_STATUS_IS_OK(status)) {
629                 goto fail;
630         }
631
632         ntlmssp_want_feature(es->s.ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
633         es->s.ntlmssp_state->neg_flags |= (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL);
634
635         if (!NT_STATUS_IS_OK(status = ntlmssp_set_username(es->s.ntlmssp_state, user))) {
636                 goto fail;
637         }
638         if (!NT_STATUS_IS_OK(status = ntlmssp_set_domain(es->s.ntlmssp_state, domain))) {
639                 goto fail;
640         }
641         if (!NT_STATUS_IS_OK(status = ntlmssp_set_password(es->s.ntlmssp_state, pass))) {
642                 goto fail;
643         }
644
645         do {
646                 status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
647                 data_blob_free(&blob_in);
648                 data_blob_free(&param_out);
649                 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
650                         NTSTATUS trans_status = enc_blob_send_receive(cli,
651                                                                         &blob_out,
652                                                                         &blob_in,
653                                                                         &param_out);
654                         if (!NT_STATUS_EQUAL(trans_status,
655                                         NT_STATUS_MORE_PROCESSING_REQUIRED) &&
656                                         !NT_STATUS_IS_OK(trans_status)) {
657                                 status = trans_status;
658                         } else {
659                                 if (param_out.length == 2) {
660                                         es->enc_ctx_num = SVAL(param_out.data, 0);
661                                 }
662                         }
663                 }
664                 data_blob_free(&blob_out);
665         } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
666
667         data_blob_free(&blob_in);
668
669         if (NT_STATUS_IS_OK(status)) {
670                 /* Replace the old state, if any. */
671                 if (cli->trans_enc_state) {
672                         common_free_encryption_state(&cli->trans_enc_state);
673                 }
674                 cli->trans_enc_state = es;
675                 cli->trans_enc_state->enc_on = True;
676                 es = NULL;
677         }
678
679   fail:
680
681         common_free_encryption_state(&es);
682         return status;
683 }
684
685 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
686
687 #ifndef SMB_GSS_REQUIRED_FLAGS
688 #define SMB_GSS_REQUIRED_FLAGS (GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)
689 #endif
690
691 /******************************************************************************
692  Get client gss blob to send to a server.
693 ******************************************************************************/
694
695 static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
696                                 const char *service,
697                                 const char *host,
698                                 NTSTATUS status_in,
699                                 DATA_BLOB spnego_blob_in,
700                                 DATA_BLOB *p_blob_out)
701 {
702         const char *krb_mechs[] = {OID_KERBEROS5, NULL};
703         OM_uint32 ret;
704         OM_uint32 min;
705         gss_name_t srv_name;
706         gss_buffer_desc input_name;
707         gss_buffer_desc *p_tok_in;
708         gss_buffer_desc tok_out, tok_in;
709         DATA_BLOB blob_out = data_blob_null;
710         DATA_BLOB blob_in = data_blob_null;
711         char *host_princ_s = NULL;
712         OM_uint32 ret_flags = 0;
713         NTSTATUS status = NT_STATUS_OK;
714
715         gss_OID_desc nt_hostbased_service =
716         {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
717
718         memset(&tok_out, '\0', sizeof(tok_out));
719
720         /* Get a ticket for the service@host */
721         if (asprintf(&host_princ_s, "%s@%s", service, host) == -1) {
722                 return NT_STATUS_NO_MEMORY;
723         }
724
725         input_name.value = host_princ_s;
726         input_name.length = strlen(host_princ_s) + 1;
727
728         ret = gss_import_name(&min,
729                                 &input_name,
730                                 &nt_hostbased_service,
731                                 &srv_name);
732
733         if (ret != GSS_S_COMPLETE) {
734                 SAFE_FREE(host_princ_s);
735                 return map_nt_error_from_gss(ret, min);
736         }
737
738         if (spnego_blob_in.length == 0) {
739                 p_tok_in = GSS_C_NO_BUFFER;
740         } else {
741                 /* Remove the SPNEGO wrapper */
742                 if (!spnego_parse_auth_response(spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
743                         status = NT_STATUS_UNSUCCESSFUL;
744                         goto fail;
745                 }
746                 tok_in.value = blob_in.data;
747                 tok_in.length = blob_in.length;
748                 p_tok_in = &tok_in;
749         }
750
751         ret = gss_init_sec_context(&min,
752                                 GSS_C_NO_CREDENTIAL, /* Use our default cred. */
753                                 &es->s.gss_state->gss_ctx,
754                                 srv_name,
755                                 GSS_C_NO_OID, /* default OID. */
756                                 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG,
757                                 GSS_C_INDEFINITE,       /* requested ticket lifetime. */
758                                 NULL,   /* no channel bindings */
759                                 p_tok_in,
760                                 NULL,   /* ignore mech type */
761                                 &tok_out,
762                                 &ret_flags,
763                                 NULL);  /* ignore time_rec */
764
765         status = map_nt_error_from_gss(ret, min);
766         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
767                 ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
768                 DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
769                         ads_errstr(adss)));
770                 goto fail;
771         }
772
773         if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
774                 status = NT_STATUS_ACCESS_DENIED;
775         }
776
777         blob_out = data_blob(tok_out.value, tok_out.length);
778
779         /* Wrap in an SPNEGO wrapper */
780         *p_blob_out = gen_negTokenTarg(krb_mechs, blob_out);
781
782   fail:
783
784         data_blob_free(&blob_out);
785         data_blob_free(&blob_in);
786         SAFE_FREE(host_princ_s);
787         gss_release_name(&min, &srv_name);
788         if (tok_out.value) {
789                 gss_release_buffer(&min, &tok_out);
790         }
791         return status;
792 }
793
794 /******************************************************************************
795  Start a SPNEGO gssapi encryption context.
796 ******************************************************************************/
797
798 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
799 {
800         DATA_BLOB blob_recv = data_blob_null;
801         DATA_BLOB blob_send = data_blob_null;
802         DATA_BLOB param_out = data_blob_null;
803         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
804         fstring fqdn;
805         const char *servicename;
806         struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
807
808         if (!es) {
809                 return NT_STATUS_NO_MEMORY;
810         }
811
812         name_to_fqdn(fqdn, cli->desthost);
813         strlower_m(fqdn);
814
815         servicename = "cifs";
816         status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
817         if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
818                 servicename = "host";
819                 status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
820                 if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
821                         goto fail;
822                 }
823         }
824
825         do {
826                 data_blob_free(&blob_recv);
827                 status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
828                 if (param_out.length == 2) {
829                         es->enc_ctx_num = SVAL(param_out.data, 0);
830                 }
831                 data_blob_free(&blob_send);
832                 status = make_cli_gss_blob(es, servicename, fqdn, status, blob_recv, &blob_send);
833         } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
834         data_blob_free(&blob_recv);
835
836         if (NT_STATUS_IS_OK(status)) {
837                 /* Replace the old state, if any. */
838                 if (cli->trans_enc_state) {
839                         common_free_encryption_state(&cli->trans_enc_state);
840                 }
841                 cli->trans_enc_state = es;
842                 cli->trans_enc_state->enc_on = True;
843                 es = NULL;
844         }
845
846   fail:
847
848         common_free_encryption_state(&es);
849         return status;
850 }
851 #else
852 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
853 {
854         return NT_STATUS_NOT_SUPPORTED;
855 }
856 #endif
857
858 /********************************************************************
859  Ensure a connection is encrypted.
860 ********************************************************************/
861
862 NTSTATUS cli_force_encryption(struct cli_state *c,
863                         const char *username,
864                         const char *password,
865                         const char *domain)
866 {
867         uint16 major, minor;
868         uint32 caplow, caphigh;
869         NTSTATUS status;
870
871         if (!SERVER_HAS_UNIX_CIFS(c)) {
872                 return NT_STATUS_NOT_SUPPORTED;
873         }
874
875         status = cli_unix_extensions_version(c, &major, &minor, &caplow,
876                                              &caphigh);
877         if (!NT_STATUS_IS_OK(status)) {
878                 DEBUG(10, ("cli_force_encryption: cli_unix_extensions_version "
879                            "returned %s\n", nt_errstr(status)));
880                 return NT_STATUS_UNKNOWN_REVISION;
881         }
882
883         if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
884                 return NT_STATUS_UNSUPPORTED_COMPRESSION;
885         }
886
887         if (c->use_kerberos) {
888                 return cli_gss_smb_encryption_start(c);
889         }
890         return cli_raw_ntlm_smb_encryption_start(c,
891                                         username,
892                                         password,
893                                         domain);
894 }