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