s3:libsmb: use anonymous authentication via spnego if possible
[sfrench/samba-autobuild/.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    Copyright (C) Andrew Bartlett 2011
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 "libsmb/libsmb.h"
24 #include "../lib/util/tevent_ntstatus.h"
25 #include "async_smb.h"
26 #include "../libcli/smb/smb_seal.h"
27 #include "trans2.h"
28 #include "auth_generic.h"
29 #include "auth/gensec/gensec.h"
30 #include "../libcli/smb/smbXcli_base.h"
31 #include "auth/credentials/credentials.h"
32
33 /****************************************************************************
34  Get UNIX extensions version info.
35 ****************************************************************************/
36
37 struct cli_unix_extensions_version_state {
38         struct cli_state *cli;
39         uint16_t setup[1];
40         uint8_t param[2];
41         uint16_t major, minor;
42         uint32_t caplow, caphigh;
43 };
44
45 static void cli_unix_extensions_version_done(struct tevent_req *subreq);
46
47 struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx,
48                                                     struct tevent_context *ev,
49                                                     struct cli_state *cli)
50 {
51         struct tevent_req *req, *subreq;
52         struct cli_unix_extensions_version_state *state;
53
54         req = tevent_req_create(mem_ctx, &state,
55                                 struct cli_unix_extensions_version_state);
56         if (req == NULL) {
57                 return NULL;
58         }
59         state->cli = cli;
60         SSVAL(state->setup, 0, TRANSACT2_QFSINFO);
61         SSVAL(state->param, 0, SMB_QUERY_CIFS_UNIX_INFO);
62
63         subreq = cli_trans_send(state, ev, cli, SMBtrans2,
64                                 NULL, 0, 0, 0,
65                                 state->setup, 1, 0,
66                                 state->param, 2, 0,
67                                 NULL, 0, 560);
68         if (tevent_req_nomem(subreq, req)) {
69                 return tevent_req_post(req, ev);
70         }
71         tevent_req_set_callback(subreq, cli_unix_extensions_version_done, req);
72         return req;
73 }
74
75 static void cli_unix_extensions_version_done(struct tevent_req *subreq)
76 {
77         struct tevent_req *req = tevent_req_callback_data(
78                 subreq, struct tevent_req);
79         struct cli_unix_extensions_version_state *state = tevent_req_data(
80                 req, struct cli_unix_extensions_version_state);
81         uint8_t *data;
82         uint32_t num_data;
83         NTSTATUS status;
84
85         status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
86                                 NULL, 0, NULL, &data, 12, &num_data);
87         TALLOC_FREE(subreq);
88         if (!NT_STATUS_IS_OK(status)) {
89                 tevent_req_nterror(req, status);
90                 return;
91         }
92
93         state->major = SVAL(data, 0);
94         state->minor = SVAL(data, 2);
95         state->caplow = IVAL(data, 4);
96         state->caphigh = IVAL(data, 8);
97         TALLOC_FREE(data);
98         tevent_req_done(req);
99 }
100
101 NTSTATUS cli_unix_extensions_version_recv(struct tevent_req *req,
102                                           uint16_t *pmajor, uint16_t *pminor,
103                                           uint32_t *pcaplow,
104                                           uint32_t *pcaphigh)
105 {
106         struct cli_unix_extensions_version_state *state = tevent_req_data(
107                 req, struct cli_unix_extensions_version_state);
108         NTSTATUS status;
109
110         if (tevent_req_is_nterror(req, &status)) {
111                 return status;
112         }
113         *pmajor = state->major;
114         *pminor = state->minor;
115         *pcaplow = state->caplow;
116         *pcaphigh = state->caphigh;
117         state->cli->server_posix_capabilities = *pcaplow;
118         return NT_STATUS_OK;
119 }
120
121 NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16_t *pmajor,
122                                      uint16_t *pminor, uint32_t *pcaplow,
123                                      uint32_t *pcaphigh)
124 {
125         TALLOC_CTX *frame = talloc_stackframe();
126         struct tevent_context *ev;
127         struct tevent_req *req;
128         NTSTATUS status = NT_STATUS_OK;
129
130         if (smbXcli_conn_has_async_calls(cli->conn)) {
131                 /*
132                  * Can't use sync call while an async call is in flight
133                  */
134                 status = NT_STATUS_INVALID_PARAMETER;
135                 goto fail;
136         }
137
138         ev = samba_tevent_context_init(frame);
139         if (ev == NULL) {
140                 status = NT_STATUS_NO_MEMORY;
141                 goto fail;
142         }
143
144         req = cli_unix_extensions_version_send(frame, ev, cli);
145         if (req == NULL) {
146                 status = NT_STATUS_NO_MEMORY;
147                 goto fail;
148         }
149
150         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
151                 goto fail;
152         }
153
154         status = cli_unix_extensions_version_recv(req, pmajor, pminor, pcaplow,
155                                                   pcaphigh);
156  fail:
157         TALLOC_FREE(frame);
158         return status;
159 }
160
161 /****************************************************************************
162  Set UNIX extensions capabilities.
163 ****************************************************************************/
164
165 struct cli_set_unix_extensions_capabilities_state {
166         struct cli_state *cli;
167         uint16_t setup[1];
168         uint8_t param[4];
169         uint8_t data[12];
170 };
171
172 static void cli_set_unix_extensions_capabilities_done(
173         struct tevent_req *subreq);
174
175 struct tevent_req *cli_set_unix_extensions_capabilities_send(
176         TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
177         uint16_t major, uint16_t minor, uint32_t caplow, uint32_t caphigh)
178 {
179         struct tevent_req *req, *subreq;
180         struct cli_set_unix_extensions_capabilities_state *state;
181
182         req = tevent_req_create(
183                 mem_ctx, &state,
184                 struct cli_set_unix_extensions_capabilities_state);
185         if (req == NULL) {
186                 return NULL;
187         }
188
189         state->cli = cli;
190         SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO);
191
192         SSVAL(state->param, 0, 0);
193         SSVAL(state->param, 2, SMB_SET_CIFS_UNIX_INFO);
194
195         SSVAL(state->data, 0, major);
196         SSVAL(state->data, 2, minor);
197         SIVAL(state->data, 4, caplow);
198         SIVAL(state->data, 8, caphigh);
199
200         subreq = cli_trans_send(state, ev, cli, SMBtrans2,
201                                 NULL, 0, 0, 0,
202                                 state->setup, 1, 0,
203                                 state->param, 4, 0,
204                                 state->data, 12, 560);
205         if (tevent_req_nomem(subreq, req)) {
206                 return tevent_req_post(req, ev);
207         }
208         tevent_req_set_callback(
209                 subreq, cli_set_unix_extensions_capabilities_done, req);
210         return req;
211 }
212
213 static void cli_set_unix_extensions_capabilities_done(
214         struct tevent_req *subreq)
215 {
216         struct tevent_req *req = tevent_req_callback_data(
217                 subreq, struct tevent_req);
218         struct cli_set_unix_extensions_capabilities_state *state = tevent_req_data(
219                 req, struct cli_set_unix_extensions_capabilities_state);
220
221         NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL,
222                                          NULL, 0, NULL, NULL, 0, NULL);
223         if (NT_STATUS_IS_OK(status)) {
224                 state->cli->requested_posix_capabilities = IVAL(state->data, 4);
225         }
226         tevent_req_simple_finish_ntstatus(subreq, status);
227 }
228
229 NTSTATUS cli_set_unix_extensions_capabilities_recv(struct tevent_req *req)
230 {
231         return tevent_req_simple_recv_ntstatus(req);
232 }
233
234 NTSTATUS cli_set_unix_extensions_capabilities(struct cli_state *cli,
235                                               uint16_t major, uint16_t minor,
236                                               uint32_t caplow, uint32_t caphigh)
237 {
238         struct tevent_context *ev;
239         struct tevent_req *req;
240         NTSTATUS status = NT_STATUS_NO_MEMORY;
241
242         if (smbXcli_conn_has_async_calls(cli->conn)) {
243                 return NT_STATUS_INVALID_PARAMETER;
244         }
245         ev = samba_tevent_context_init(talloc_tos());
246         if (ev == NULL) {
247                 goto fail;
248         }
249         req = cli_set_unix_extensions_capabilities_send(
250                 ev, ev, cli, major, minor, caplow, caphigh);
251         if (req == NULL) {
252                 goto fail;
253         }
254         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
255                 goto fail;
256         }
257         status = cli_set_unix_extensions_capabilities_recv(req);
258 fail:
259         TALLOC_FREE(ev);
260         return status;
261 }
262
263 struct cli_get_fs_attr_info_state {
264         uint16_t setup[1];
265         uint8_t param[2];
266         uint32_t fs_attr;
267 };
268
269 static void cli_get_fs_attr_info_done(struct tevent_req *subreq);
270
271 struct tevent_req *cli_get_fs_attr_info_send(TALLOC_CTX *mem_ctx,
272                                              struct tevent_context *ev,
273                                              struct cli_state *cli)
274 {
275         struct tevent_req *subreq, *req;
276         struct cli_get_fs_attr_info_state *state;
277
278         req = tevent_req_create(mem_ctx, &state,
279                                 struct cli_get_fs_attr_info_state);
280         if (req == NULL) {
281                 return NULL;
282         }
283         SSVAL(state->setup+0, 0, TRANSACT2_QFSINFO);
284         SSVAL(state->param+0, 0, SMB_QUERY_FS_ATTRIBUTE_INFO);
285
286         subreq = cli_trans_send(state, ev, cli, SMBtrans2,
287                                 NULL, 0, 0, 0,
288                                 state->setup, 1, 0,
289                                 state->param, 2, 0,
290                                 NULL, 0, 560);
291         if (tevent_req_nomem(subreq, req)) {
292                 return tevent_req_post(req, ev);
293         }
294         tevent_req_set_callback(subreq, cli_get_fs_attr_info_done, req);
295         return req;
296 }
297
298 static void cli_get_fs_attr_info_done(struct tevent_req *subreq)
299 {
300         struct tevent_req *req = tevent_req_callback_data(
301                 subreq, struct tevent_req);
302         struct cli_get_fs_attr_info_state *state = tevent_req_data(
303                 req, struct cli_get_fs_attr_info_state);
304         uint8_t *data;
305         uint32_t num_data;
306         NTSTATUS status;
307
308         status = cli_trans_recv(subreq, talloc_tos(), NULL, NULL, 0, NULL,
309                                 NULL, 0, NULL, &data, 12, &num_data);
310         TALLOC_FREE(subreq);
311         if (!NT_STATUS_IS_OK(status)) {
312                 tevent_req_nterror(req, status);
313                 return;
314         }
315         state->fs_attr = IVAL(data, 0);
316         TALLOC_FREE(data);
317         tevent_req_done(req);
318 }
319
320 NTSTATUS cli_get_fs_attr_info_recv(struct tevent_req *req, uint32_t *fs_attr)
321 {
322         struct cli_get_fs_attr_info_state *state = tevent_req_data(
323                 req, struct cli_get_fs_attr_info_state);
324         NTSTATUS status;
325
326         if (tevent_req_is_nterror(req, &status)) {
327                 return status;
328         }
329         *fs_attr = state->fs_attr;
330         return NT_STATUS_OK;
331 }
332
333 NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
334 {
335         struct tevent_context *ev;
336         struct tevent_req *req;
337         NTSTATUS status = NT_STATUS_NO_MEMORY;
338
339         if (smbXcli_conn_has_async_calls(cli->conn)) {
340                 return NT_STATUS_INVALID_PARAMETER;
341         }
342         ev = samba_tevent_context_init(talloc_tos());
343         if (ev == NULL) {
344                 goto fail;
345         }
346         req = cli_get_fs_attr_info_send(ev, ev, cli);
347         if (req == NULL) {
348                 goto fail;
349         }
350         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
351                 goto fail;
352         }
353         status = cli_get_fs_attr_info_recv(req, fs_attr);
354 fail:
355         TALLOC_FREE(ev);
356         return status;
357 }
358
359 NTSTATUS cli_get_fs_volume_info(struct cli_state *cli,
360                                 TALLOC_CTX *mem_ctx,
361                                 char **_volume_name,
362                                 uint32_t *pserial_number,
363                                 time_t *pdate)
364 {
365         NTSTATUS status;
366         uint16_t recv_flags2;
367         uint16_t setup[1];
368         uint8_t param[2];
369         uint8_t *rdata;
370         uint32_t rdata_count;
371         unsigned int nlen;
372         char *volume_name = NULL;
373
374         SSVAL(setup, 0, TRANSACT2_QFSINFO);
375         SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
376
377         status = cli_trans(talloc_tos(), cli, SMBtrans2,
378                            NULL, 0, 0, 0,
379                            setup, 1, 0,
380                            param, 2, 0,
381                            NULL, 0, 560,
382                            &recv_flags2,
383                            NULL, 0, NULL,
384                            NULL, 0, NULL,
385                            &rdata, 18, &rdata_count);
386         if (!NT_STATUS_IS_OK(status)) {
387                 return status;
388         }
389
390         if (pdate) {
391                 struct timespec ts;
392                 ts = interpret_long_date((char *)rdata);
393                 *pdate = ts.tv_sec;
394         }
395         if (pserial_number) {
396                 *pserial_number = IVAL(rdata,8);
397         }
398         nlen = IVAL(rdata,12);
399         if (nlen > (rdata_count - 18)) {
400                 TALLOC_FREE(rdata);
401                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
402         }
403
404         clistr_pull_talloc(mem_ctx,
405                            (const char *)rdata,
406                            recv_flags2,
407                            &volume_name,
408                            rdata + 18,
409                            nlen, STR_UNICODE);
410         if (volume_name == NULL) {
411                 status = map_nt_error_from_unix(errno);
412                 TALLOC_FREE(rdata);
413                 return status;
414         }
415
416         /* todo: but not yet needed
417          *       return the other stuff
418          */
419
420         *_volume_name = volume_name;
421         TALLOC_FREE(rdata);
422         return NT_STATUS_OK;
423 }
424
425 NTSTATUS cli_get_fs_full_size_info(struct cli_state *cli,
426                                    uint64_t *total_allocation_units,
427                                    uint64_t *caller_allocation_units,
428                                    uint64_t *actual_allocation_units,
429                                    uint64_t *sectors_per_allocation_unit,
430                                    uint64_t *bytes_per_sector)
431 {
432         uint16_t setup[1];
433         uint8_t param[2];
434         uint8_t *rdata = NULL;
435         uint32_t rdata_count;
436         NTSTATUS status;
437
438         SSVAL(setup, 0, TRANSACT2_QFSINFO);
439         SSVAL(param, 0, SMB_FS_FULL_SIZE_INFORMATION);
440
441         status = cli_trans(talloc_tos(), cli, SMBtrans2,
442                            NULL, 0, 0, 0,
443                            setup, 1, 0, /* setup */
444                            param, 2, 0,  /* param */
445                            NULL, 0, 560, /* data */
446                            NULL,
447                            NULL, 0, NULL, /* rsetup */
448                            NULL, 0, NULL, /* rparam */
449                            &rdata, 32, &rdata_count);  /* rdata */
450         if (!NT_STATUS_IS_OK(status)) {
451                 goto fail;
452         }
453
454         if (total_allocation_units) {
455                 *total_allocation_units = BIG_UINT(rdata, 0);
456         }
457         if (caller_allocation_units) {
458                 *caller_allocation_units = BIG_UINT(rdata,8);
459         }
460         if (actual_allocation_units) {
461                 *actual_allocation_units = BIG_UINT(rdata,16);
462         }
463         if (sectors_per_allocation_unit) {
464                 *sectors_per_allocation_unit = IVAL(rdata,24);
465         }
466         if (bytes_per_sector) {
467                 *bytes_per_sector = IVAL(rdata,28);
468         }
469
470 fail:
471         TALLOC_FREE(rdata);
472         return status;
473 }
474
475 NTSTATUS cli_get_posix_fs_info(struct cli_state *cli,
476                                uint32_t *optimal_transfer_size,
477                                uint32_t *block_size,
478                                uint64_t *total_blocks,
479                                uint64_t *blocks_available,
480                                uint64_t *user_blocks_available,
481                                uint64_t *total_file_nodes,
482                                uint64_t *free_file_nodes,
483                                uint64_t *fs_identifier)
484 {
485         uint16_t setup[1];
486         uint8_t param[2];
487         uint8_t *rdata = NULL;
488         uint32_t rdata_count;
489         NTSTATUS status;
490
491         SSVAL(setup, 0, TRANSACT2_QFSINFO);
492         SSVAL(param,0,SMB_QUERY_POSIX_FS_INFO);
493
494         status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, 0, 0, 0,
495                            setup, 1, 0,
496                            param, 2, 0,
497                            NULL, 0, 560,
498                            NULL,
499                            NULL, 0, NULL, /* rsetup */
500                            NULL, 0, NULL, /* rparam */
501                            &rdata, 56, &rdata_count);
502         if (!NT_STATUS_IS_OK(status)) {
503                 return status;
504         }
505
506         if (optimal_transfer_size) {
507                 *optimal_transfer_size = IVAL(rdata, 0);
508         }
509         if (block_size) {
510                 *block_size = IVAL(rdata,4);
511         }
512         if (total_blocks) {
513                 *total_blocks = BIG_UINT(rdata,8);
514         }
515         if (blocks_available) {
516                 *blocks_available = BIG_UINT(rdata,16);
517         }
518         if (user_blocks_available) {
519                 *user_blocks_available = BIG_UINT(rdata,24);
520         }
521         if (total_file_nodes) {
522                 *total_file_nodes = BIG_UINT(rdata,32);
523         }
524         if (free_file_nodes) {
525                 *free_file_nodes = BIG_UINT(rdata,40);
526         }
527         if (fs_identifier) {
528                 *fs_identifier = BIG_UINT(rdata,48);
529         }
530         return NT_STATUS_OK;
531 }
532
533
534 /******************************************************************************
535  Send/receive the request encryption blob.
536 ******************************************************************************/
537
538 static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
539 {
540         uint16_t setup[1];
541         uint8_t param[4];
542         uint8_t *rparam=NULL, *rdata=NULL;
543         uint32_t num_rparam, num_rdata;
544         NTSTATUS status;
545
546         SSVAL(setup+0, 0, TRANSACT2_SETFSINFO);
547         SSVAL(param,0,0);
548         SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
549
550         status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, 0, 0, 0,
551                            setup, 1, 0,
552                            param, 4, 2,
553                            (uint8_t *)in->data, in->length, CLI_BUFFER_SIZE,
554                            NULL,          /* recv_flags */
555                            NULL, 0, NULL, /* rsetup */
556                            &rparam, 0, &num_rparam,
557                            &rdata, 0, &num_rdata);
558
559         if (!NT_STATUS_IS_OK(status) &&
560             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
561                 return status;
562         }
563
564         *out = data_blob(rdata, num_rdata);
565         *param_out = data_blob(rparam, num_rparam);
566
567         TALLOC_FREE(rparam);
568         TALLOC_FREE(rdata);
569         return status;
570 }
571
572 /******************************************************************************
573  Start a raw ntlmssp encryption.
574 ******************************************************************************/
575
576 NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli, 
577                                 const char *user,
578                                 const char *pass,
579                                 const char *domain)
580 {
581         DATA_BLOB blob_in = data_blob_null;
582         DATA_BLOB blob_out = data_blob_null;
583         DATA_BLOB param_out = data_blob_null;
584         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
585         struct auth_generic_state *auth_generic_state;
586         struct smb_trans_enc_state *es = talloc_zero(NULL, struct smb_trans_enc_state);
587         if (!es) {
588                 return NT_STATUS_NO_MEMORY;
589         }
590         status = auth_generic_client_prepare(es,
591                                              &auth_generic_state);
592         if (!NT_STATUS_IS_OK(status)) {
593                 goto fail;
594         }
595
596         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SESSION_KEY);
597         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
598
599         if (!NT_STATUS_IS_OK(status = auth_generic_set_username(auth_generic_state, user))) {
600                 goto fail;
601         }
602         if (!NT_STATUS_IS_OK(status = auth_generic_set_domain(auth_generic_state, domain))) {
603                 goto fail;
604         }
605         if (!NT_STATUS_IS_OK(status = auth_generic_set_password(auth_generic_state, pass))) {
606                 goto fail;
607         }
608
609         if (!NT_STATUS_IS_OK(status = auth_generic_client_start(auth_generic_state, GENSEC_OID_NTLMSSP))) {
610                 goto fail;
611         }
612
613         do {
614                 status = gensec_update(auth_generic_state->gensec_security, auth_generic_state,
615                                        blob_in, &blob_out);
616                 data_blob_free(&blob_in);
617                 data_blob_free(&param_out);
618                 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
619                         NTSTATUS trans_status = enc_blob_send_receive(cli,
620                                                                         &blob_out,
621                                                                         &blob_in,
622                                                                         &param_out);
623                         if (!NT_STATUS_EQUAL(trans_status,
624                                         NT_STATUS_MORE_PROCESSING_REQUIRED) &&
625                                         !NT_STATUS_IS_OK(trans_status)) {
626                                 status = trans_status;
627                         } else {
628                                 if (param_out.length == 2) {
629                                         es->enc_ctx_num = SVAL(param_out.data, 0);
630                                 }
631                         }
632                 }
633                 data_blob_free(&blob_out);
634         } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
635
636         data_blob_free(&blob_in);
637
638         if (NT_STATUS_IS_OK(status)) {
639                 es->enc_on = true;
640                 /* Replace the old state, if any. */
641                 /* We only need the gensec_security part from here.
642                  * es is a malloc()ed pointer, so we cannot make
643                  * gensec_security a talloc child */
644                 es->gensec_security = talloc_move(NULL,
645                                                   &auth_generic_state->gensec_security);
646                 smb1cli_conn_set_encryption(cli->conn, es);
647                 es = NULL;
648         }
649
650   fail:
651         TALLOC_FREE(es);
652         return status;
653 }
654
655 /******************************************************************************
656  Start a SPNEGO gssapi encryption context.
657 ******************************************************************************/
658
659 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
660 {
661         DATA_BLOB blob_recv = data_blob_null;
662         DATA_BLOB blob_send = data_blob_null;
663         DATA_BLOB param_out = data_blob_null;
664         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
665         struct auth_generic_state *auth_generic_state;
666         struct smb_trans_enc_state *es = talloc_zero(NULL, struct smb_trans_enc_state);
667
668         if (!es) {
669                 return NT_STATUS_NO_MEMORY;
670         }
671
672         status = auth_generic_client_prepare(es,
673                                              &auth_generic_state);
674         if (!NT_STATUS_IS_OK(status)) {
675                 goto fail;
676         }
677
678         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SESSION_KEY);
679         gensec_want_feature(auth_generic_state->gensec_security, GENSEC_FEATURE_SEAL);
680
681         cli_credentials_set_kerberos_state(auth_generic_state->credentials, 
682                                            CRED_MUST_USE_KERBEROS);
683
684         status = gensec_set_target_service(auth_generic_state->gensec_security, "cifs");
685         if (!NT_STATUS_IS_OK(status)) {
686                 goto fail;
687         }
688
689         status = gensec_set_target_hostname(auth_generic_state->gensec_security, 
690                                             smbXcli_conn_remote_name(cli->conn));
691         if (!NT_STATUS_IS_OK(status)) {
692                 goto fail;
693         }
694
695         if (!NT_STATUS_IS_OK(status = auth_generic_client_start(auth_generic_state, GENSEC_OID_SPNEGO))) {
696                 goto fail;
697         }
698
699         status = gensec_update(auth_generic_state->gensec_security, talloc_tos(),
700                                blob_recv, &blob_send);
701
702         do {
703                 data_blob_free(&blob_recv);
704                 status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
705                 if (param_out.length == 2) {
706                         es->enc_ctx_num = SVAL(param_out.data, 0);
707                 }
708                 data_blob_free(&blob_send);
709                 status = gensec_update(auth_generic_state->gensec_security, talloc_tos(),
710                                        blob_recv, &blob_send);
711         } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
712         data_blob_free(&blob_recv);
713
714         if (NT_STATUS_IS_OK(status)) {
715                 if (!gensec_have_feature(auth_generic_state->gensec_security, 
716                                          GENSEC_FEATURE_SIGN) ||
717                     !gensec_have_feature(auth_generic_state->gensec_security, 
718                                          GENSEC_FEATURE_SEAL)) {
719                         status = NT_STATUS_ACCESS_DENIED;
720                 }
721         }
722
723         if (NT_STATUS_IS_OK(status)) {
724                 es->enc_on = true;
725                 /* Replace the old state, if any. */
726                 /* We only need the gensec_security part from here.
727                  * es is a malloc()ed pointer, so we cannot make
728                  * gensec_security a talloc child */
729                 es->gensec_security = talloc_move(es,
730                                                   &auth_generic_state->gensec_security);
731                 smb1cli_conn_set_encryption(cli->conn, es);
732                 es = NULL;
733         }
734 fail:
735         TALLOC_FREE(es);
736         return status;
737 }
738
739 /********************************************************************
740  Ensure a connection is encrypted.
741 ********************************************************************/
742
743 NTSTATUS cli_force_encryption(struct cli_state *c,
744                         const char *username,
745                         const char *password,
746                         const char *domain)
747 {
748         uint16_t major, minor;
749         uint32_t caplow, caphigh;
750         NTSTATUS status;
751
752         if (!SERVER_HAS_UNIX_CIFS(c)) {
753                 return NT_STATUS_NOT_SUPPORTED;
754         }
755
756         status = cli_unix_extensions_version(c, &major, &minor, &caplow,
757                                              &caphigh);
758         if (!NT_STATUS_IS_OK(status)) {
759                 DEBUG(10, ("cli_force_encryption: cli_unix_extensions_version "
760                            "returned %s\n", nt_errstr(status)));
761                 return NT_STATUS_UNKNOWN_REVISION;
762         }
763
764         if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
765                 return NT_STATUS_UNSUPPORTED_COMPRESSION;
766         }
767
768         if (c->use_kerberos) {
769                 return cli_gss_smb_encryption_start(c);
770         }
771         return cli_raw_ntlm_smb_encryption_start(c,
772                                         username,
773                                         password,
774                                         domain);
775 }