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