s4:upgradeprovision Rework script, and reset machine account pw
[Samba/cd1.git] / source3 / libsmb / clifsinfo.c
blobec690b4d823e9448d651508aa43f61d56585cfbc
1 /*
2 Unix SMB/CIFS implementation.
3 FS info functions
4 Copyright (C) Stefan (metze) Metzmacher 2003
5 Copyright (C) Jeremy Allison 2007
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.
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.
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/>.
21 #include "includes.h"
22 #include "../libcli/auth/spnego.h"
24 /****************************************************************************
25 Get UNIX extensions version info.
26 ****************************************************************************/
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;
35 static void cli_unix_extensions_version_done(struct tevent_req *subreq);
37 struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx,
38 struct tevent_context *ev,
39 struct cli_state *cli)
41 struct tevent_req *req, *subreq;
42 struct cli_unix_extensions_version_state *state;
44 req = tevent_req_create(mem_ctx, &state,
45 struct cli_unix_extensions_version_state);
46 if (req == NULL) {
47 return NULL;
49 SSVAL(state->setup, 0, TRANSACT2_QFSINFO);
50 SSVAL(state->param, 0, SMB_QUERY_CIFS_UNIX_INFO);
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);
60 tevent_req_set_callback(subreq, cli_unix_extensions_version_done, req);
61 return req;
64 static void cli_unix_extensions_version_done(struct tevent_req *subreq)
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;
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;
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);
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)
95 struct cli_unix_extensions_version_state *state = tevent_req_data(
96 req, struct cli_unix_extensions_version_state);
97 NTSTATUS status;
99 if (tevent_req_is_nterror(req, &status)) {
100 return status;
102 *pmajor = state->major;
103 *pminor = state->minor;
104 *pcaplow = state->caplow;
105 *pcaphigh = state->caphigh;
106 return NT_STATUS_OK;
109 NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor,
110 uint16 *pminor, uint32 *pcaplow,
111 uint32 *pcaphigh)
113 TALLOC_CTX *frame = talloc_stackframe();
114 struct event_context *ev;
115 struct tevent_req *req;
116 NTSTATUS status = NT_STATUS_OK;
118 if (cli_has_async_calls(cli)) {
120 * Can't use sync call while an async call is in flight
122 status = NT_STATUS_INVALID_PARAMETER;
123 goto fail;
126 ev = event_context_init(frame);
127 if (ev == NULL) {
128 status = NT_STATUS_NO_MEMORY;
129 goto fail;
132 req = cli_unix_extensions_version_send(frame, ev, cli);
133 if (req == NULL) {
134 status = NT_STATUS_NO_MEMORY;
135 goto fail;
138 if (!tevent_req_poll(req, ev)) {
139 status = map_nt_error_from_unix(errno);
140 goto fail;
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;
148 fail:
149 TALLOC_FREE(frame);
150 if (!NT_STATUS_IS_OK(status)) {
151 cli_set_error(cli, status);
153 return status;
156 /****************************************************************************
157 Set UNIX extensions capabilities.
158 ****************************************************************************/
160 struct cli_set_unix_extensions_capabilities_state {
161 uint16_t setup[1];
162 uint8_t param[4];
163 uint8_t data[12];
166 static void cli_set_unix_extensions_capabilities_done(
167 struct tevent_req *subreq);
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)
173 struct tevent_req *req, *subreq;
174 struct cli_set_unix_extensions_capabilities_state *state;
176 req = tevent_req_create(
177 mem_ctx, &state,
178 struct cli_set_unix_extensions_capabilities_state);
179 if (req == NULL) {
180 return NULL;
183 SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO);
185 SSVAL(state->param, 0, 0);
186 SSVAL(state->param, 2, SMB_SET_CIFS_UNIX_INFO);
188 SSVAL(state->data, 0, major);
189 SSVAL(state->data, 2, minor);
190 SIVAL(state->data, 4, caplow);
191 SIVAL(state->data, 8, caphigh);
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);
201 tevent_req_set_callback(
202 subreq, cli_set_unix_extensions_capabilities_done, req);
203 return req;
206 static void cli_set_unix_extensions_capabilities_done(
207 struct tevent_req *subreq)
209 NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, 0, NULL,
210 NULL, 0, NULL, NULL, 0, NULL);
211 tevent_req_simple_finish_ntstatus(subreq, status);
214 NTSTATUS cli_set_unix_extensions_capabilities_recv(struct tevent_req *req)
216 return tevent_req_simple_recv_ntstatus(req);
219 NTSTATUS cli_set_unix_extensions_capabilities(struct cli_state *cli,
220 uint16 major, uint16 minor,
221 uint32 caplow, uint32 caphigh)
223 struct tevent_context *ev;
224 struct tevent_req *req;
225 NTSTATUS status = NT_STATUS_NO_MEMORY;
227 if (cli_has_async_calls(cli)) {
228 return NT_STATUS_INVALID_PARAMETER;
230 ev = tevent_context_init(talloc_tos());
231 if (ev == NULL) {
232 goto fail;
234 req = cli_set_unix_extensions_capabilities_send(
235 ev, ev, cli, major, minor, caplow, caphigh);
236 if (req == NULL) {
237 goto fail;
239 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
240 goto fail;
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);
248 return status;
251 struct cli_get_fs_attr_info_state {
252 uint16_t setup[1];
253 uint8_t param[2];
254 uint32_t fs_attr;
257 static void cli_get_fs_attr_info_done(struct tevent_req *subreq);
259 struct tevent_req *cli_get_fs_attr_info_send(TALLOC_CTX *mem_ctx,
260 struct tevent_context *ev,
261 struct cli_state *cli)
263 struct tevent_req *subreq, *req;
264 struct cli_get_fs_attr_info_state *state;
266 req = tevent_req_create(mem_ctx, &state,
267 struct cli_get_fs_attr_info_state);
268 if (req == NULL) {
269 return NULL;
271 SSVAL(state->setup+0, 0, TRANSACT2_QFSINFO);
272 SSVAL(state->param+0, 0, SMB_QUERY_FS_ATTRIBUTE_INFO);
274 subreq = cli_trans_send(state, ev, cli, SMBtrans2,
275 NULL, 0, 0, 0,
276 state->setup, 1, 0,
277 state->param, 2, 0,
278 NULL, 0, 560);
279 if (tevent_req_nomem(subreq, req)) {
280 return tevent_req_post(req, ev);
282 tevent_req_set_callback(subreq, cli_get_fs_attr_info_done, req);
283 return req;
286 static void cli_get_fs_attr_info_done(struct tevent_req *subreq)
288 struct tevent_req *req = tevent_req_callback_data(
289 subreq, struct tevent_req);
290 struct cli_get_fs_attr_info_state *state = tevent_req_data(
291 req, struct cli_get_fs_attr_info_state);
292 uint8_t *data;
293 uint32_t num_data;
294 NTSTATUS status;
296 status = cli_trans_recv(subreq, talloc_tos(), NULL, 0, NULL,
297 NULL, 0, NULL, &data, 12, &num_data);
298 TALLOC_FREE(subreq);
299 if (!NT_STATUS_IS_OK(status)) {
300 tevent_req_nterror(req, status);
301 return;
303 state->fs_attr = IVAL(data, 0);
304 TALLOC_FREE(data);
305 tevent_req_done(req);
308 NTSTATUS cli_get_fs_attr_info_recv(struct tevent_req *req, uint32_t *fs_attr)
310 struct cli_get_fs_attr_info_state *state = tevent_req_data(
311 req, struct cli_get_fs_attr_info_state);
312 NTSTATUS status;
314 if (tevent_req_is_nterror(req, &status)) {
315 return status;
317 *fs_attr = state->fs_attr;
318 return NT_STATUS_OK;
321 NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
323 struct tevent_context *ev;
324 struct tevent_req *req;
325 NTSTATUS status = NT_STATUS_NO_MEMORY;
327 if (cli_has_async_calls(cli)) {
328 return NT_STATUS_INVALID_PARAMETER;
330 ev = tevent_context_init(talloc_tos());
331 if (ev == NULL) {
332 goto fail;
334 req = cli_get_fs_attr_info_send(ev, ev, cli);
335 if (req == NULL) {
336 goto fail;
338 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
339 goto fail;
341 status = cli_get_fs_attr_info_recv(req, fs_attr);
342 fail:
343 TALLOC_FREE(ev);
344 if (!NT_STATUS_IS_OK(status)) {
345 cli_set_error(cli, status);
347 return status;
350 bool cli_get_fs_volume_info_old(struct cli_state *cli, fstring volume_name, uint32 *pserial_number)
352 bool ret = False;
353 uint16 setup;
354 char param[2];
355 char *rparam=NULL, *rdata=NULL;
356 unsigned int rparam_count=0, rdata_count=0;
357 unsigned char nlen;
359 setup = TRANSACT2_QFSINFO;
361 SSVAL(param,0,SMB_INFO_VOLUME);
363 if (!cli_send_trans(cli, SMBtrans2,
364 NULL,
365 0, 0,
366 &setup, 1, 0,
367 param, 2, 0,
368 NULL, 0, 560)) {
369 goto cleanup;
372 if (!cli_receive_trans(cli, SMBtrans2,
373 &rparam, &rparam_count,
374 &rdata, &rdata_count)) {
375 goto cleanup;
378 if (cli_is_error(cli)) {
379 ret = False;
380 goto cleanup;
381 } else {
382 ret = True;
385 if (rdata_count < 5) {
386 goto cleanup;
389 if (pserial_number) {
390 *pserial_number = IVAL(rdata,0);
392 nlen = CVAL(rdata,l2_vol_cch);
393 clistr_pull(cli->inbuf, volume_name, rdata + l2_vol_szVolLabel,
394 sizeof(fstring), nlen, STR_NOALIGN);
396 /* todo: but not yet needed
397 * return the other stuff
400 cleanup:
401 SAFE_FREE(rparam);
402 SAFE_FREE(rdata);
404 return ret;
407 bool cli_get_fs_volume_info(struct cli_state *cli, fstring volume_name, uint32 *pserial_number, time_t *pdate)
409 bool ret = False;
410 uint16 setup;
411 char param[2];
412 char *rparam=NULL, *rdata=NULL;
413 unsigned int rparam_count=0, rdata_count=0;
414 unsigned int nlen;
416 setup = TRANSACT2_QFSINFO;
418 SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
420 if (!cli_send_trans(cli, SMBtrans2,
421 NULL,
422 0, 0,
423 &setup, 1, 0,
424 param, 2, 0,
425 NULL, 0, 560)) {
426 goto cleanup;
429 if (!cli_receive_trans(cli, SMBtrans2,
430 &rparam, &rparam_count,
431 &rdata, &rdata_count)) {
432 goto cleanup;
435 if (cli_is_error(cli)) {
436 ret = False;
437 goto cleanup;
438 } else {
439 ret = True;
442 if (rdata_count < 19) {
443 goto cleanup;
446 if (pdate) {
447 struct timespec ts;
448 ts = interpret_long_date(rdata);
449 *pdate = ts.tv_sec;
451 if (pserial_number) {
452 *pserial_number = IVAL(rdata,8);
454 nlen = IVAL(rdata,12);
455 clistr_pull(cli->inbuf, volume_name, rdata + 18, sizeof(fstring),
456 nlen, STR_UNICODE);
458 /* todo: but not yet needed
459 * return the other stuff
462 cleanup:
463 SAFE_FREE(rparam);
464 SAFE_FREE(rdata);
466 return ret;
469 bool cli_get_fs_full_size_info(struct cli_state *cli,
470 uint64_t *total_allocation_units,
471 uint64_t *caller_allocation_units,
472 uint64_t *actual_allocation_units,
473 uint64_t *sectors_per_allocation_unit,
474 uint64_t *bytes_per_sector)
476 bool ret = False;
477 uint16 setup;
478 char param[2];
479 char *rparam=NULL, *rdata=NULL;
480 unsigned int rparam_count=0, rdata_count=0;
482 setup = TRANSACT2_QFSINFO;
484 SSVAL(param,0,SMB_FS_FULL_SIZE_INFORMATION);
486 if (!cli_send_trans(cli, SMBtrans2,
487 NULL,
488 0, 0,
489 &setup, 1, 0,
490 param, 2, 0,
491 NULL, 0, 560)) {
492 goto cleanup;
495 if (!cli_receive_trans(cli, SMBtrans2,
496 &rparam, &rparam_count,
497 &rdata, &rdata_count)) {
498 goto cleanup;
501 if (cli_is_error(cli)) {
502 ret = False;
503 goto cleanup;
504 } else {
505 ret = True;
508 if (rdata_count != 32) {
509 goto cleanup;
512 if (total_allocation_units) {
513 *total_allocation_units = BIG_UINT(rdata, 0);
515 if (caller_allocation_units) {
516 *caller_allocation_units = BIG_UINT(rdata,8);
518 if (actual_allocation_units) {
519 *actual_allocation_units = BIG_UINT(rdata,16);
521 if (sectors_per_allocation_unit) {
522 *sectors_per_allocation_unit = IVAL(rdata,24);
524 if (bytes_per_sector) {
525 *bytes_per_sector = IVAL(rdata,28);
528 cleanup:
529 SAFE_FREE(rparam);
530 SAFE_FREE(rdata);
532 return ret;
535 bool cli_get_posix_fs_info(struct cli_state *cli,
536 uint32 *optimal_transfer_size,
537 uint32 *block_size,
538 uint64_t *total_blocks,
539 uint64_t *blocks_available,
540 uint64_t *user_blocks_available,
541 uint64_t *total_file_nodes,
542 uint64_t *free_file_nodes,
543 uint64_t *fs_identifier)
545 bool ret = False;
546 uint16 setup;
547 char param[2];
548 char *rparam=NULL, *rdata=NULL;
549 unsigned int rparam_count=0, rdata_count=0;
551 setup = TRANSACT2_QFSINFO;
553 SSVAL(param,0,SMB_QUERY_POSIX_FS_INFO);
555 if (!cli_send_trans(cli, SMBtrans2,
556 NULL,
557 0, 0,
558 &setup, 1, 0,
559 param, 2, 0,
560 NULL, 0, 560)) {
561 goto cleanup;
564 if (!cli_receive_trans(cli, SMBtrans2,
565 &rparam, &rparam_count,
566 &rdata, &rdata_count)) {
567 goto cleanup;
570 if (cli_is_error(cli)) {
571 ret = False;
572 goto cleanup;
573 } else {
574 ret = True;
577 if (rdata_count != 56) {
578 goto cleanup;
581 if (optimal_transfer_size) {
582 *optimal_transfer_size = IVAL(rdata, 0);
584 if (block_size) {
585 *block_size = IVAL(rdata,4);
587 if (total_blocks) {
588 *total_blocks = BIG_UINT(rdata,8);
590 if (blocks_available) {
591 *blocks_available = BIG_UINT(rdata,16);
593 if (user_blocks_available) {
594 *user_blocks_available = BIG_UINT(rdata,24);
596 if (total_file_nodes) {
597 *total_file_nodes = BIG_UINT(rdata,32);
599 if (free_file_nodes) {
600 *free_file_nodes = BIG_UINT(rdata,40);
602 if (fs_identifier) {
603 *fs_identifier = BIG_UINT(rdata,48);
606 cleanup:
607 SAFE_FREE(rparam);
608 SAFE_FREE(rdata);
610 return ret;
614 /******************************************************************************
615 Send/receive the request encryption blob.
616 ******************************************************************************/
618 static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
620 uint16 setup;
621 char param[4];
622 char *rparam=NULL, *rdata=NULL;
623 unsigned int rparam_count=0, rdata_count=0;
624 NTSTATUS status = NT_STATUS_OK;
626 setup = TRANSACT2_SETFSINFO;
628 SSVAL(param,0,0);
629 SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
631 if (!cli_send_trans(cli, SMBtrans2,
632 NULL,
633 0, 0,
634 &setup, 1, 0,
635 param, 4, 0,
636 (char *)in->data, in->length, CLI_BUFFER_SIZE)) {
637 status = cli_nt_error(cli);
638 goto out;
641 if (!cli_receive_trans(cli, SMBtrans2,
642 &rparam, &rparam_count,
643 &rdata, &rdata_count)) {
644 status = cli_nt_error(cli);
645 goto out;
648 if (cli_is_error(cli)) {
649 status = cli_nt_error(cli);
650 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
651 goto out;
655 *out = data_blob(rdata, rdata_count);
656 *param_out = data_blob(rparam, rparam_count);
658 out:
660 SAFE_FREE(rparam);
661 SAFE_FREE(rdata);
662 return status;
665 /******************************************************************************
666 Make a client state struct.
667 ******************************************************************************/
669 static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type smb_enc_type)
671 struct smb_trans_enc_state *es = NULL;
672 es = SMB_MALLOC_P(struct smb_trans_enc_state);
673 if (!es) {
674 return NULL;
676 ZERO_STRUCTP(es);
677 es->smb_enc_type = smb_enc_type;
679 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
680 if (smb_enc_type == SMB_TRANS_ENC_GSS) {
681 es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
682 if (!es->s.gss_state) {
683 SAFE_FREE(es);
684 return NULL;
686 ZERO_STRUCTP(es->s.gss_state);
688 #endif
689 return es;
692 /******************************************************************************
693 Start a raw ntlmssp encryption.
694 ******************************************************************************/
696 NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli,
697 const char *user,
698 const char *pass,
699 const char *domain)
701 DATA_BLOB blob_in = data_blob_null;
702 DATA_BLOB blob_out = data_blob_null;
703 DATA_BLOB param_out = data_blob_null;
704 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
705 struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_NTLM);
707 if (!es) {
708 return NT_STATUS_NO_MEMORY;
710 status = ntlmssp_client_start(&es->s.ntlmssp_state);
711 if (!NT_STATUS_IS_OK(status)) {
712 goto fail;
715 ntlmssp_want_feature(es->s.ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
716 es->s.ntlmssp_state->neg_flags |= (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL);
718 if (!NT_STATUS_IS_OK(status = ntlmssp_set_username(es->s.ntlmssp_state, user))) {
719 goto fail;
721 if (!NT_STATUS_IS_OK(status = ntlmssp_set_domain(es->s.ntlmssp_state, domain))) {
722 goto fail;
724 if (!NT_STATUS_IS_OK(status = ntlmssp_set_password(es->s.ntlmssp_state, pass))) {
725 goto fail;
728 do {
729 status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
730 data_blob_free(&blob_in);
731 data_blob_free(&param_out);
732 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
733 NTSTATUS trans_status = enc_blob_send_receive(cli,
734 &blob_out,
735 &blob_in,
736 &param_out);
737 if (!NT_STATUS_EQUAL(trans_status,
738 NT_STATUS_MORE_PROCESSING_REQUIRED) &&
739 !NT_STATUS_IS_OK(trans_status)) {
740 status = trans_status;
741 } else {
742 if (param_out.length == 2) {
743 es->enc_ctx_num = SVAL(param_out.data, 0);
747 data_blob_free(&blob_out);
748 } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
750 data_blob_free(&blob_in);
752 if (NT_STATUS_IS_OK(status)) {
753 /* Replace the old state, if any. */
754 if (cli->trans_enc_state) {
755 common_free_encryption_state(&cli->trans_enc_state);
757 cli->trans_enc_state = es;
758 cli->trans_enc_state->enc_on = True;
759 es = NULL;
762 fail:
764 common_free_encryption_state(&es);
765 return status;
768 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
770 #ifndef SMB_GSS_REQUIRED_FLAGS
771 #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)
772 #endif
774 /******************************************************************************
775 Get client gss blob to send to a server.
776 ******************************************************************************/
778 static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
779 const char *service,
780 const char *host,
781 NTSTATUS status_in,
782 DATA_BLOB spnego_blob_in,
783 DATA_BLOB *p_blob_out)
785 const char *krb_mechs[] = {OID_KERBEROS5, NULL};
786 OM_uint32 ret;
787 OM_uint32 min;
788 gss_name_t srv_name;
789 gss_buffer_desc input_name;
790 gss_buffer_desc *p_tok_in;
791 gss_buffer_desc tok_out, tok_in;
792 DATA_BLOB blob_out = data_blob_null;
793 DATA_BLOB blob_in = data_blob_null;
794 char *host_princ_s = NULL;
795 OM_uint32 ret_flags = 0;
796 NTSTATUS status = NT_STATUS_OK;
798 gss_OID_desc nt_hostbased_service =
799 {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
801 memset(&tok_out, '\0', sizeof(tok_out));
803 /* Get a ticket for the service@host */
804 if (asprintf(&host_princ_s, "%s@%s", service, host) == -1) {
805 return NT_STATUS_NO_MEMORY;
808 input_name.value = host_princ_s;
809 input_name.length = strlen(host_princ_s) + 1;
811 ret = gss_import_name(&min,
812 &input_name,
813 &nt_hostbased_service,
814 &srv_name);
816 if (ret != GSS_S_COMPLETE) {
817 SAFE_FREE(host_princ_s);
818 return map_nt_error_from_gss(ret, min);
821 if (spnego_blob_in.length == 0) {
822 p_tok_in = GSS_C_NO_BUFFER;
823 } else {
824 /* Remove the SPNEGO wrapper */
825 if (!spnego_parse_auth_response(spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
826 status = NT_STATUS_UNSUCCESSFUL;
827 goto fail;
829 tok_in.value = blob_in.data;
830 tok_in.length = blob_in.length;
831 p_tok_in = &tok_in;
834 ret = gss_init_sec_context(&min,
835 GSS_C_NO_CREDENTIAL, /* Use our default cred. */
836 &es->s.gss_state->gss_ctx,
837 srv_name,
838 GSS_C_NO_OID, /* default OID. */
839 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG,
840 GSS_C_INDEFINITE, /* requested ticket lifetime. */
841 NULL, /* no channel bindings */
842 p_tok_in,
843 NULL, /* ignore mech type */
844 &tok_out,
845 &ret_flags,
846 NULL); /* ignore time_rec */
848 status = map_nt_error_from_gss(ret, min);
849 if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
850 ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
851 DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
852 ads_errstr(adss)));
853 goto fail;
856 if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
857 status = NT_STATUS_ACCESS_DENIED;
860 blob_out = data_blob(tok_out.value, tok_out.length);
862 /* Wrap in an SPNEGO wrapper */
863 *p_blob_out = gen_negTokenTarg(krb_mechs, blob_out);
865 fail:
867 data_blob_free(&blob_out);
868 data_blob_free(&blob_in);
869 SAFE_FREE(host_princ_s);
870 gss_release_name(&min, &srv_name);
871 if (tok_out.value) {
872 gss_release_buffer(&min, &tok_out);
874 return status;
877 /******************************************************************************
878 Start a SPNEGO gssapi encryption context.
879 ******************************************************************************/
881 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
883 DATA_BLOB blob_recv = data_blob_null;
884 DATA_BLOB blob_send = data_blob_null;
885 DATA_BLOB param_out = data_blob_null;
886 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
887 fstring fqdn;
888 const char *servicename;
889 struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
891 if (!es) {
892 return NT_STATUS_NO_MEMORY;
895 name_to_fqdn(fqdn, cli->desthost);
896 strlower_m(fqdn);
898 servicename = "cifs";
899 status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
900 if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
901 servicename = "host";
902 status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
903 if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
904 goto fail;
908 do {
909 data_blob_free(&blob_recv);
910 status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
911 if (param_out.length == 2) {
912 es->enc_ctx_num = SVAL(param_out.data, 0);
914 data_blob_free(&blob_send);
915 status = make_cli_gss_blob(es, servicename, fqdn, status, blob_recv, &blob_send);
916 } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
917 data_blob_free(&blob_recv);
919 if (NT_STATUS_IS_OK(status)) {
920 /* Replace the old state, if any. */
921 if (cli->trans_enc_state) {
922 common_free_encryption_state(&cli->trans_enc_state);
924 cli->trans_enc_state = es;
925 cli->trans_enc_state->enc_on = True;
926 es = NULL;
929 fail:
931 common_free_encryption_state(&es);
932 return status;
934 #else
935 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
937 return NT_STATUS_NOT_SUPPORTED;
939 #endif
941 /********************************************************************
942 Ensure a connection is encrypted.
943 ********************************************************************/
945 NTSTATUS cli_force_encryption(struct cli_state *c,
946 const char *username,
947 const char *password,
948 const char *domain)
950 uint16 major, minor;
951 uint32 caplow, caphigh;
952 NTSTATUS status;
954 if (!SERVER_HAS_UNIX_CIFS(c)) {
955 return NT_STATUS_NOT_SUPPORTED;
958 status = cli_unix_extensions_version(c, &major, &minor, &caplow,
959 &caphigh);
960 if (!NT_STATUS_IS_OK(status)) {
961 DEBUG(10, ("cli_force_encryption: cli_unix_extensions_version "
962 "returned %s\n", nt_errstr(status)));
963 return NT_STATUS_UNKNOWN_REVISION;
966 if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
967 return NT_STATUS_UNSUPPORTED_COMPRESSION;
970 if (c->use_kerberos) {
971 return cli_gss_smb_encryption_start(c);
973 return cli_raw_ntlm_smb_encryption_start(c,
974 username,
975 password,
976 domain);