Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
[ira/wip.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
23 /****************************************************************************
24  Get UNIX extensions version info.
25 ****************************************************************************/
26
27 bool cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 *pminor,
28                                         uint32 *pcaplow, uint32 *pcaphigh)
29 {
30         bool ret = False;
31         uint16 setup;
32         char param[2];
33         char *rparam=NULL, *rdata=NULL;
34         unsigned int rparam_count=0, rdata_count=0;
35
36         setup = TRANSACT2_QFSINFO;
37
38         SSVAL(param,0,SMB_QUERY_CIFS_UNIX_INFO);
39
40         if (!cli_send_trans(cli, SMBtrans2,
41                     NULL,
42                     0, 0,
43                     &setup, 1, 0,
44                     param, 2, 0,
45                     NULL, 0, 560)) {
46                 goto cleanup;
47         }
48
49         if (!cli_receive_trans(cli, SMBtrans2,
50                               &rparam, &rparam_count,
51                               &rdata, &rdata_count)) {
52                 goto cleanup;
53         }
54
55         if (cli_is_error(cli)) {
56                 ret = False;
57                 goto cleanup;
58         } else {
59                 ret = True;
60         }
61
62         if (rdata_count < 12) {
63                 goto cleanup;
64         }
65
66         *pmajor = SVAL(rdata,0);
67         *pminor = SVAL(rdata,2);
68         cli->posix_capabilities = *pcaplow = IVAL(rdata,4);
69         *pcaphigh = IVAL(rdata,8);
70
71         /* todo: but not yet needed
72          *       return the other stuff
73          */
74
75 cleanup:
76         SAFE_FREE(rparam);
77         SAFE_FREE(rdata);
78
79         return ret;
80 }
81
82 /****************************************************************************
83  Set UNIX extensions capabilities.
84 ****************************************************************************/
85
86 bool cli_set_unix_extensions_capabilities(struct cli_state *cli, uint16 major, uint16 minor,
87                                         uint32 caplow, uint32 caphigh)
88 {
89         bool ret = False;
90         uint16 setup;
91         char param[4];
92         char data[12];
93         char *rparam=NULL, *rdata=NULL;
94         unsigned int rparam_count=0, rdata_count=0;
95
96         setup = TRANSACT2_SETFSINFO;
97
98         SSVAL(param,0,0);
99         SSVAL(param,2,SMB_SET_CIFS_UNIX_INFO);
100
101         SSVAL(data,0,major);
102         SSVAL(data,2,minor);
103         SIVAL(data,4,caplow);
104         SIVAL(data,8,caphigh);
105
106         if (!cli_send_trans(cli, SMBtrans2,
107                     NULL,
108                     0, 0,
109                     &setup, 1, 0,
110                     param, 4, 0,
111                     data, 12, 560)) {
112                 goto cleanup;
113         }
114
115         if (!cli_receive_trans(cli, SMBtrans2,
116                               &rparam, &rparam_count,
117                               &rdata, &rdata_count)) {
118                 goto cleanup;
119         }
120
121         if (cli_is_error(cli)) {
122                 ret = False;
123                 goto cleanup;
124         } else {
125                 ret = True;
126         }
127
128 cleanup:
129         SAFE_FREE(rparam);
130         SAFE_FREE(rdata);
131
132         return ret;
133 }
134
135 bool cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr)
136 {
137         bool ret = False;
138         uint16 setup;
139         char param[2];
140         char *rparam=NULL, *rdata=NULL;
141         unsigned int rparam_count=0, rdata_count=0;
142
143         if (!cli||!fs_attr)
144                 smb_panic("cli_get_fs_attr_info() called with NULL Pionter!");
145
146         setup = TRANSACT2_QFSINFO;
147
148         SSVAL(param,0,SMB_QUERY_FS_ATTRIBUTE_INFO);
149
150         if (!cli_send_trans(cli, SMBtrans2,
151                     NULL,
152                     0, 0,
153                     &setup, 1, 0,
154                     param, 2, 0,
155                     NULL, 0, 560)) {
156                 goto cleanup;
157         }
158
159         if (!cli_receive_trans(cli, SMBtrans2,
160                               &rparam, &rparam_count,
161                               &rdata, &rdata_count)) {
162                 goto cleanup;
163         }
164
165         if (cli_is_error(cli)) {
166                 ret = False;
167                 goto cleanup;
168         } else {
169                 ret = True;
170         }
171
172         if (rdata_count < 12) {
173                 goto cleanup;
174         }
175
176         *fs_attr = IVAL(rdata,0);
177
178         /* todo: but not yet needed
179          *       return the other stuff
180          */
181
182 cleanup:
183         SAFE_FREE(rparam);
184         SAFE_FREE(rdata);
185
186         return ret;
187 }
188
189 bool cli_get_fs_volume_info_old(struct cli_state *cli, fstring volume_name, uint32 *pserial_number)
190 {
191         bool ret = False;
192         uint16 setup;
193         char param[2];
194         char *rparam=NULL, *rdata=NULL;
195         unsigned int rparam_count=0, rdata_count=0;
196         unsigned char nlen;
197
198         setup = TRANSACT2_QFSINFO;
199
200         SSVAL(param,0,SMB_INFO_VOLUME);
201
202         if (!cli_send_trans(cli, SMBtrans2,
203                     NULL,
204                     0, 0,
205                     &setup, 1, 0,
206                     param, 2, 0,
207                     NULL, 0, 560)) {
208                 goto cleanup;
209         }
210
211         if (!cli_receive_trans(cli, SMBtrans2,
212                               &rparam, &rparam_count,
213                               &rdata, &rdata_count)) {
214                 goto cleanup;
215         }
216
217         if (cli_is_error(cli)) {
218                 ret = False;
219                 goto cleanup;
220         } else {
221                 ret = True;
222         }
223
224         if (rdata_count < 5) {
225                 goto cleanup;
226         }
227
228         if (pserial_number) {
229                 *pserial_number = IVAL(rdata,0);
230         }
231         nlen = CVAL(rdata,l2_vol_cch);
232         clistr_pull(cli, volume_name, rdata + l2_vol_szVolLabel, sizeof(fstring), nlen, STR_NOALIGN);
233
234         /* todo: but not yet needed
235          *       return the other stuff
236          */
237
238 cleanup:
239         SAFE_FREE(rparam);
240         SAFE_FREE(rdata);
241
242         return ret;
243 }
244
245 bool cli_get_fs_volume_info(struct cli_state *cli, fstring volume_name, uint32 *pserial_number, time_t *pdate)
246 {
247         bool ret = False;
248         uint16 setup;
249         char param[2];
250         char *rparam=NULL, *rdata=NULL;
251         unsigned int rparam_count=0, rdata_count=0;
252         unsigned int nlen;
253
254         setup = TRANSACT2_QFSINFO;
255
256         SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
257
258         if (!cli_send_trans(cli, SMBtrans2,
259                     NULL,
260                     0, 0,
261                     &setup, 1, 0,
262                     param, 2, 0,
263                     NULL, 0, 560)) {
264                 goto cleanup;
265         }
266
267         if (!cli_receive_trans(cli, SMBtrans2,
268                               &rparam, &rparam_count,
269                               &rdata, &rdata_count)) {
270                 goto cleanup;
271         }
272
273         if (cli_is_error(cli)) {
274                 ret = False;
275                 goto cleanup;
276         } else {
277                 ret = True;
278         }
279
280         if (rdata_count < 19) {
281                 goto cleanup;
282         }
283
284         if (pdate) {
285                 struct timespec ts;
286                 ts = interpret_long_date(rdata);
287                 *pdate = ts.tv_sec;
288         }
289         if (pserial_number) {
290                 *pserial_number = IVAL(rdata,8);
291         }
292         nlen = IVAL(rdata,12);
293         clistr_pull(cli, volume_name, rdata + 18, sizeof(fstring), nlen, STR_UNICODE);
294
295         /* todo: but not yet needed
296          *       return the other stuff
297          */
298
299 cleanup:
300         SAFE_FREE(rparam);
301         SAFE_FREE(rdata);
302
303         return ret;
304 }
305
306 /******************************************************************************
307  Send/receive the request encryption blob.
308 ******************************************************************************/
309
310 static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
311 {
312         uint16 setup;
313         char param[4];
314         char *rparam=NULL, *rdata=NULL;
315         unsigned int rparam_count=0, rdata_count=0;
316         NTSTATUS status = NT_STATUS_OK;
317
318         setup = TRANSACT2_SETFSINFO;
319
320         SSVAL(param,0,0);
321         SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
322
323         if (!cli_send_trans(cli, SMBtrans2,
324                                 NULL,
325                                 0, 0,
326                                 &setup, 1, 0,
327                                 param, 4, 0,
328                                 (char *)in->data, in->length, CLI_BUFFER_SIZE)) {
329                 status = cli_nt_error(cli);
330                 goto out;
331         }
332
333         if (!cli_receive_trans(cli, SMBtrans2,
334                                 &rparam, &rparam_count,
335                                 &rdata, &rdata_count)) {
336                 status = cli_nt_error(cli);
337                 goto out;
338         }
339
340         if (cli_is_error(cli)) {
341                 status = cli_nt_error(cli);
342                 if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
343                         goto out;
344                 }
345         }
346
347         *out = data_blob(rdata, rdata_count);
348         *param_out = data_blob(rparam, rparam_count);
349
350   out:
351
352         SAFE_FREE(rparam);
353         SAFE_FREE(rdata);
354         return status;
355 }
356
357 /******************************************************************************
358  Make a client state struct.
359 ******************************************************************************/
360
361 static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type smb_enc_type)
362 {
363         struct smb_trans_enc_state *es = NULL;
364         es = SMB_MALLOC_P(struct smb_trans_enc_state);
365         if (!es) {
366                 return NULL;
367         }
368         ZERO_STRUCTP(es);
369         es->smb_enc_type = smb_enc_type;
370
371         if (smb_enc_type == SMB_TRANS_ENC_GSS) {
372 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
373                 es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
374                 if (!es->s.gss_state) {
375                         SAFE_FREE(es);
376                         return NULL;
377                 }
378                 ZERO_STRUCTP(es->s.gss_state);
379 #else
380                 DEBUG(0,("make_cli_enc_state: no krb5 compiled.\n"));
381                 SAFE_FREE(es);
382                 return NULL;
383 #endif
384         }
385         return es;
386 }
387
388 /******************************************************************************
389  Start a raw ntlmssp encryption.
390 ******************************************************************************/
391
392 NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli, 
393                                 const char *user,
394                                 const char *pass,
395                                 const char *domain)
396 {
397         DATA_BLOB blob_in = data_blob_null;
398         DATA_BLOB blob_out = data_blob_null;
399         DATA_BLOB param_out = data_blob_null;
400         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
401         struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_NTLM);
402
403         if (!es) {
404                 return NT_STATUS_NO_MEMORY;
405         }
406         status = ntlmssp_client_start(&es->s.ntlmssp_state);
407         if (!NT_STATUS_IS_OK(status)) {
408                 goto fail;
409         }
410
411         ntlmssp_want_feature(es->s.ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
412         es->s.ntlmssp_state->neg_flags |= (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL);
413
414         if (!NT_STATUS_IS_OK(status = ntlmssp_set_username(es->s.ntlmssp_state, user))) {
415                 goto fail;
416         }
417         if (!NT_STATUS_IS_OK(status = ntlmssp_set_domain(es->s.ntlmssp_state, domain))) {
418                 goto fail;
419         }
420         if (!NT_STATUS_IS_OK(status = ntlmssp_set_password(es->s.ntlmssp_state, pass))) {
421                 goto fail;
422         }
423
424         do {
425                 status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
426                 data_blob_free(&blob_in);
427                 data_blob_free(&param_out);
428                 if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
429                         NTSTATUS trans_status = enc_blob_send_receive(cli,
430                                                                         &blob_out,
431                                                                         &blob_in,
432                                                                         &param_out);
433                         if (!NT_STATUS_EQUAL(trans_status,
434                                         NT_STATUS_MORE_PROCESSING_REQUIRED) &&
435                                         !NT_STATUS_IS_OK(trans_status)) {
436                                 status = trans_status;
437                         } else {
438                                 if (param_out.length == 2) {
439                                         es->enc_ctx_num = SVAL(param_out.data, 0);
440                                 }
441                         }
442                 }
443                 data_blob_free(&blob_out);
444         } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
445
446         data_blob_free(&blob_in);
447
448         if (NT_STATUS_IS_OK(status)) {
449                 /* Replace the old state, if any. */
450                 if (cli->trans_enc_state) {
451                         common_free_encryption_state(&cli->trans_enc_state);
452                 }
453                 cli->trans_enc_state = es;
454                 cli->trans_enc_state->enc_on = True;
455                 es = NULL;
456         }
457
458   fail:
459
460         common_free_encryption_state(&es);
461         return status;
462 }
463
464 #if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
465
466 #ifndef SMB_GSS_REQUIRED_FLAGS
467 #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)
468 #endif
469
470 /******************************************************************************
471  Get client gss blob to send to a server.
472 ******************************************************************************/
473
474 static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
475                                 const char *service,
476                                 const char *host,
477                                 NTSTATUS status_in,
478                                 DATA_BLOB spnego_blob_in,
479                                 DATA_BLOB *p_blob_out)
480 {
481         const char *krb_mechs[] = {OID_KERBEROS5, NULL};
482         OM_uint32 ret;
483         OM_uint32 min;
484         gss_name_t srv_name;
485         gss_buffer_desc input_name;
486         gss_buffer_desc *p_tok_in;
487         gss_buffer_desc tok_out, tok_in;
488         DATA_BLOB blob_out = data_blob_null;
489         DATA_BLOB blob_in = data_blob_null;
490         char *host_princ_s = NULL;
491         OM_uint32 ret_flags = 0;
492         NTSTATUS status = NT_STATUS_OK;
493
494         gss_OID_desc nt_hostbased_service =
495         {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
496
497         memset(&tok_out, '\0', sizeof(tok_out));
498
499         /* Get a ticket for the service@host */
500         asprintf(&host_princ_s, "%s@%s", service, host);
501         if (host_princ_s == NULL) {
502                 return NT_STATUS_NO_MEMORY;
503         }
504
505         input_name.value = host_princ_s;
506         input_name.length = strlen(host_princ_s) + 1;
507
508         ret = gss_import_name(&min,
509                                 &input_name,
510                                 &nt_hostbased_service,
511                                 &srv_name);
512
513         if (ret != GSS_S_COMPLETE) {
514                 SAFE_FREE(host_princ_s);
515                 return map_nt_error_from_gss(ret, min);
516         }
517
518         if (spnego_blob_in.length == 0) {
519                 p_tok_in = GSS_C_NO_BUFFER;
520         } else {
521                 /* Remove the SPNEGO wrapper */
522                 if (!spnego_parse_auth_response(spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
523                         status = NT_STATUS_UNSUCCESSFUL;
524                         goto fail;
525                 }
526                 tok_in.value = blob_in.data;
527                 tok_in.length = blob_in.length;
528                 p_tok_in = &tok_in;
529         }
530
531         ret = gss_init_sec_context(&min,
532                                 GSS_C_NO_CREDENTIAL, /* Use our default cred. */
533                                 &es->s.gss_state->gss_ctx,
534                                 srv_name,
535                                 GSS_C_NO_OID, /* default OID. */
536                                 GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
537                                 GSS_C_INDEFINITE,       /* requested ticket lifetime. */
538                                 NULL,   /* no channel bindings */
539                                 p_tok_in,
540                                 NULL,   /* ignore mech type */
541                                 &tok_out,
542                                 &ret_flags,
543                                 NULL);  /* ignore time_rec */
544
545         status = map_nt_error_from_gss(ret, min);
546         if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
547                 ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
548                 DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
549                         ads_errstr(adss)));
550                 goto fail;
551         }
552
553         if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
554                 status = NT_STATUS_ACCESS_DENIED;
555         }
556
557         blob_out = data_blob(tok_out.value, tok_out.length);
558
559         /* Wrap in an SPNEGO wrapper */
560         *p_blob_out = gen_negTokenTarg(krb_mechs, blob_out);
561
562   fail:
563
564         data_blob_free(&blob_out);
565         data_blob_free(&blob_in);
566         SAFE_FREE(host_princ_s);
567         gss_release_name(&min, &srv_name);
568         if (tok_out.value) {
569                 gss_release_buffer(&min, &tok_out);
570         }
571         return status;
572 }
573
574 /******************************************************************************
575  Start a SPNEGO gssapi encryption context.
576 ******************************************************************************/
577
578 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
579 {
580         DATA_BLOB blob_recv = data_blob_null;
581         DATA_BLOB blob_send = data_blob_null;
582         DATA_BLOB param_out = data_blob_null;
583         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
584         fstring fqdn;
585         const char *servicename;
586         struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
587
588         if (!es) {
589                 return NT_STATUS_NO_MEMORY;
590         }
591
592         name_to_fqdn(fqdn, cli->desthost);
593         strlower_m(fqdn);
594
595         servicename = "cifs";
596         status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
597         if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
598                 servicename = "host";
599                 status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
600                 if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
601                         goto fail;
602                 }
603         }
604
605         do {
606                 data_blob_free(&blob_recv);
607                 status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
608                 if (param_out.length == 2) {
609                         es->enc_ctx_num = SVAL(param_out.data, 0);
610                 }
611                 data_blob_free(&blob_send);
612                 status = make_cli_gss_blob(es, servicename, fqdn, status, blob_recv, &blob_send);
613         } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
614         data_blob_free(&blob_recv);
615
616         if (NT_STATUS_IS_OK(status)) {
617                 /* Replace the old state, if any. */
618                 if (cli->trans_enc_state) {
619                         common_free_encryption_state(&cli->trans_enc_state);
620                 }
621                 cli->trans_enc_state = es;
622                 cli->trans_enc_state->enc_on = True;
623                 es = NULL;
624         }
625
626   fail:
627
628         common_free_encryption_state(&es);
629         return status;
630 }
631 #else
632 NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
633 {
634         return NT_STATUS_NOT_SUPPORTED;
635 }
636 #endif
637
638 /********************************************************************
639  Ensure a connection is encrypted.
640 ********************************************************************/
641
642 NTSTATUS cli_force_encryption(struct cli_state *c,
643                         const char *username,
644                         const char *password,
645                         const char *domain)
646 {
647         uint16 major, minor;
648         uint32 caplow, caphigh;
649
650         if (!SERVER_HAS_UNIX_CIFS(c)) {
651                 return NT_STATUS_NOT_SUPPORTED;
652         }
653
654         if (!cli_unix_extensions_version(c, &major, &minor, &caplow, &caphigh)) {
655                 return NT_STATUS_UNKNOWN_REVISION;
656         }
657
658         if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
659                 return NT_STATUS_UNSUPPORTED_COMPRESSION;
660         }
661
662         if (c->use_kerberos) {
663                 return cli_gss_smb_encryption_start(c);
664         }
665         return cli_raw_ntlm_smb_encryption_start(c,
666                                         username,
667                                         password,
668                                         domain);
669 }