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