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