Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : status reporting
4 : Copyright (C) Andrew Tridgell 1994-1998
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 :
19 : Revision History:
20 :
21 : 12 aug 96: Erik.Devriendt@te6.siemens.be
22 : added support for shared memory implementation of share mode locking
23 :
24 : 21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe)
25 : Added -L (locks only) -S (shares only) flags and code
26 :
27 : */
28 :
29 : /*
30 : * This program reports current SMB connections
31 : */
32 :
33 : #include "includes.h"
34 : #include "lib/util/server_id.h"
35 : #include "smbd/globals.h"
36 : #include "system/filesys.h"
37 : #include "lib/cmdline/cmdline.h"
38 : #include "dbwrap/dbwrap.h"
39 : #include "dbwrap/dbwrap_open.h"
40 : #include "../libcli/security/security.h"
41 : #include "session.h"
42 : #include "locking/share_mode_lock.h"
43 : #include "locking/proto.h"
44 : #include "messages.h"
45 : #include "librpc/gen_ndr/open_files.h"
46 : #include "smbd/smbd.h"
47 : #include "librpc/gen_ndr/notify.h"
48 : #include "conn_tdb.h"
49 : #include "serverid.h"
50 : #include "status_profile.h"
51 : #include "status.h"
52 : #include "status_json.h"
53 : #include "smbd/notifyd/notifyd_db.h"
54 : #include "cmdline_contexts.h"
55 : #include "locking/leases_db.h"
56 : #include "lib/util/string_wrappers.h"
57 : #include "lib/param/param.h"
58 :
59 : #ifdef HAVE_JANSSON
60 : #include <jansson.h>
61 : #include "audit_logging.h" /* various JSON helpers */
62 : #include "auth/common_auth.h"
63 : #endif /* HAVE_JANSSON */
64 :
65 : #define SMB_MAXPIDS 2048
66 : static uid_t Ucrit_uid = 0; /* added by OH */
67 : static struct server_id Ucrit_pid[SMB_MAXPIDS]; /* Ugly !!! */ /* added by OH */
68 : static int Ucrit_MaxPid=0; /* added by OH */
69 : static unsigned int Ucrit_IsActive = 0; /* added by OH */
70 :
71 : static bool verbose, brief;
72 : static bool shares_only; /* Added by RJS */
73 : static bool locks_only; /* Added by RJS */
74 : static bool processes_only;
75 : static bool show_brl;
76 : static bool numeric_only;
77 : static bool do_checks = true;
78 :
79 : const char *username = NULL;
80 :
81 : /* added by OH */
82 0 : static void Ucrit_addUid(uid_t uid)
83 : {
84 0 : Ucrit_uid = uid;
85 0 : Ucrit_IsActive = 1;
86 0 : }
87 :
88 22 : static unsigned int Ucrit_checkUid(uid_t uid)
89 : {
90 22 : if ( !Ucrit_IsActive )
91 22 : return 1;
92 :
93 0 : if ( uid == Ucrit_uid )
94 0 : return 1;
95 :
96 0 : return 0;
97 : }
98 :
99 10 : static unsigned int Ucrit_checkPid(struct server_id pid)
100 : {
101 : int i;
102 :
103 10 : if ( !Ucrit_IsActive )
104 10 : return 1;
105 :
106 0 : for (i=0;i<Ucrit_MaxPid;i++) {
107 0 : if (server_id_equal(&pid, &Ucrit_pid[i])) {
108 0 : return 1;
109 : }
110 : }
111 :
112 0 : return 0;
113 : }
114 :
115 12 : static bool Ucrit_addPid( struct server_id pid )
116 : {
117 12 : if ( !Ucrit_IsActive )
118 12 : return True;
119 :
120 0 : if ( Ucrit_MaxPid >= SMB_MAXPIDS ) {
121 0 : fprintf(stderr, "ERROR: More than %d pids for user %s!\n",
122 : SMB_MAXPIDS, uidtoname(Ucrit_uid));
123 :
124 0 : return False;
125 : }
126 :
127 0 : Ucrit_pid[Ucrit_MaxPid++] = pid;
128 :
129 0 : return True;
130 : }
131 :
132 6 : static int print_share_mode_stdout(struct traverse_state *state,
133 : const char *pid,
134 : const char *user_name,
135 : const char *denymode,
136 : int access_mask,
137 : const char *rw,
138 : const char *oplock,
139 : const char *servicepath,
140 : const char *filename,
141 : const char *timestr)
142 : {
143 6 : if (state->first) {
144 6 : d_printf("\nLocked files:\n");
145 6 : d_printf("Pid User(ID) DenyMode Access R/W Oplock SharePath Name Time\n");
146 6 : d_printf("--------------------------------------------------------------------------------------------------\n");
147 :
148 6 : state->first = false;
149 : }
150 :
151 6 : d_printf("%-11s %-9s %-10s 0x%-8x %-10s %-14s %s %s %s",
152 : pid, user_name, denymode, access_mask, rw, oplock,
153 : servicepath, filename, timestr);
154 6 : return 0;
155 : }
156 :
157 10 : static int prepare_share_mode(struct traverse_state *state)
158 : {
159 10 : if (!state->json_output) {
160 : /* only print header line if there are open files */
161 6 : state->first = true;
162 : } else {
163 4 : add_section_to_json(state, "open_files");
164 : }
165 10 : return 0;
166 : }
167 :
168 10 : static uint32_t map_share_mode_to_deny_mode(
169 : uint32_t share_access, uint32_t private_options)
170 : {
171 10 : switch (share_access & ~FILE_SHARE_DELETE) {
172 0 : case FILE_SHARE_NONE:
173 0 : return DENY_ALL;
174 0 : case FILE_SHARE_READ:
175 0 : return DENY_WRITE;
176 0 : case FILE_SHARE_WRITE:
177 0 : return DENY_READ;
178 10 : case FILE_SHARE_READ|FILE_SHARE_WRITE:
179 10 : return DENY_NONE;
180 : }
181 0 : if (private_options & NTCREATEX_FLAG_DENY_DOS) {
182 0 : return DENY_DOS;
183 0 : } else if (private_options & NTCREATEX_FLAG_DENY_FCB) {
184 0 : return DENY_FCB;
185 : }
186 :
187 0 : return (uint32_t)-1;
188 : }
189 :
190 10 : static int print_share_mode(struct file_id fid,
191 : const struct share_mode_data *d,
192 : const struct share_mode_entry *e,
193 : void *private_data)
194 : {
195 10 : const char *denymode = NULL;
196 : uint denymode_int;
197 10 : const char *oplock = NULL;
198 10 : const char *pid = NULL;
199 10 : const char *rw = NULL;
200 10 : const char *filename = NULL;
201 10 : const char *timestr = NULL;
202 10 : const char *user_str = NULL;
203 : uint32_t lstate;
204 10 : struct traverse_state *state = (struct traverse_state *)private_data;
205 :
206 10 : TALLOC_CTX *tmp_ctx = talloc_stackframe();
207 10 : if (tmp_ctx == NULL) {
208 0 : return -1;
209 : }
210 :
211 10 : if (do_checks && !is_valid_share_mode_entry(e)) {
212 0 : TALLOC_FREE(tmp_ctx);
213 0 : return 0;
214 : }
215 :
216 10 : if (do_checks && !serverid_exists(&e->pid)) {
217 : /* the process for this entry does not exist any more */
218 0 : TALLOC_FREE(tmp_ctx);
219 0 : return 0;
220 : }
221 :
222 10 : if (Ucrit_checkPid(e->pid)) {
223 : struct server_id_buf tmp;
224 10 : pid = server_id_str_buf(e->pid, &tmp);
225 10 : if (state->resolve_uids) {
226 0 : user_str = talloc_asprintf(tmp_ctx, "%s", uidtoname(e->uid));
227 : } else {
228 10 : user_str = talloc_asprintf(tmp_ctx, "%u", (unsigned int)e->uid);
229 : }
230 10 : if (user_str == NULL) {
231 0 : TALLOC_FREE(tmp_ctx);
232 0 : return -1;
233 : }
234 :
235 10 : denymode_int = map_share_mode_to_deny_mode(e->share_access,
236 10 : e->private_options);
237 10 : switch (denymode_int) {
238 10 : case DENY_NONE:
239 10 : denymode = "DENY_NONE";
240 10 : break;
241 0 : case DENY_ALL:
242 0 : denymode = "DENY_ALL";
243 0 : break;
244 0 : case DENY_DOS:
245 0 : denymode = "DENY_DOS";
246 0 : break;
247 0 : case DENY_READ:
248 0 : denymode = "DENY_READ";
249 0 : break;
250 0 : case DENY_WRITE:
251 0 : denymode = "DENY_WRITE";
252 0 : break;
253 0 : case DENY_FCB:
254 0 : denymode = "DENY_FCB";
255 0 : break;
256 0 : default: {
257 0 : denymode = talloc_asprintf(tmp_ctx,
258 : "UNKNOWN(0x%08x)",
259 : denymode_int);
260 0 : if (denymode == NULL) {
261 0 : TALLOC_FREE(tmp_ctx);
262 0 : return -1;
263 : }
264 0 : fprintf(stderr,
265 : "unknown-please report ! "
266 : "e->share_access = 0x%x, "
267 : "e->private_options = 0x%x\n",
268 0 : (unsigned int)e->share_access,
269 0 : (unsigned int)e->private_options);
270 0 : break;
271 : }
272 : }
273 10 : filename = talloc_asprintf(tmp_ctx,
274 : "%s%s",
275 10 : d->base_name,
276 10 : (d->stream_name != NULL) ? d->stream_name : "");
277 10 : if (filename == NULL) {
278 0 : TALLOC_FREE(tmp_ctx);
279 0 : return -1;
280 : }
281 10 : if ((e->access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))==
282 : (FILE_READ_DATA|FILE_WRITE_DATA)) {
283 10 : rw = "RDWR";
284 0 : } else if (e->access_mask & FILE_WRITE_DATA) {
285 0 : rw = "WRONLY";
286 : } else {
287 0 : rw = "RDONLY";
288 : }
289 :
290 10 : if (e->op_type & BATCH_OPLOCK) {
291 0 : oplock = "BATCH";
292 10 : } else if (e->op_type & EXCLUSIVE_OPLOCK) {
293 0 : oplock = "EXCLUSIVE";
294 10 : } else if (e->op_type & LEVEL_II_OPLOCK) {
295 0 : oplock = "LEVEL_II";
296 10 : } else if (e->op_type == LEASE_OPLOCK) {
297 : NTSTATUS status;
298 :
299 0 : status = leases_db_get(
300 : &e->client_guid,
301 : &e->lease_key,
302 : &d->id,
303 : &lstate, /* current_state */
304 : NULL, /* breaking */
305 : NULL, /* breaking_to_requested */
306 : NULL, /* breaking_to_required */
307 : NULL, /* lease_version */
308 : NULL); /* epoch */
309 :
310 0 : if (NT_STATUS_IS_OK(status)) {
311 0 : oplock = talloc_asprintf(tmp_ctx, "LEASE(%s%s%s)%s%s%s",
312 0 : (lstate & SMB2_LEASE_READ)?"R":"",
313 0 : (lstate & SMB2_LEASE_WRITE)?"W":"",
314 0 : (lstate & SMB2_LEASE_HANDLE)?"H":"",
315 0 : (lstate & SMB2_LEASE_READ)?"":" ",
316 0 : (lstate & SMB2_LEASE_WRITE)?"":" ",
317 0 : (lstate & SMB2_LEASE_HANDLE)?"":" ");
318 : } else {
319 0 : oplock = "LEASE STATE UNKNOWN";
320 : }
321 : } else {
322 10 : oplock = "NONE";
323 : }
324 :
325 10 : timestr = time_to_asc((time_t)e->time.tv_sec);
326 :
327 10 : if (!state->json_output) {
328 6 : print_share_mode_stdout(state,
329 : pid,
330 : user_str,
331 : denymode,
332 6 : (unsigned int)e->access_mask,
333 : rw,
334 : oplock,
335 6 : d->servicepath,
336 : filename,
337 : timestr);
338 : } else {
339 4 : print_share_mode_json(state,
340 : d,
341 : e,
342 : fid,
343 : user_str,
344 : oplock,
345 : lstate,
346 : filename);
347 : }
348 : }
349 10 : TALLOC_FREE(tmp_ctx);
350 10 : return 0;
351 : }
352 :
353 0 : static void print_brl_stdout(struct traverse_state *state,
354 : char *pid,
355 : char *id,
356 : const char *desc,
357 : intmax_t start,
358 : intmax_t size,
359 : const char *sharepath,
360 : char *fname)
361 : {
362 0 : if (state->first) {
363 0 : d_printf("Byte range locks:\n");
364 0 : d_printf("Pid dev:inode R/W start size SharePath Name\n");
365 0 : d_printf("--------------------------------------------------------------------------------\n");
366 :
367 0 : state->first = false;
368 : }
369 0 : d_printf("%-10s %-15s %-4s %-9jd %-9jd %-24s %-24s\n",
370 : pid, id, desc, start, size, sharepath, fname);
371 0 : }
372 :
373 2 : static int prepare_brl(struct traverse_state *state)
374 : {
375 2 : if (!state->json_output) {
376 : /* only print header line if there are locked files */
377 0 : state->first = true;
378 : } else {
379 2 : add_section_to_json(state, "byte_range_locks");
380 : }
381 2 : return 0;
382 : }
383 :
384 0 : static void print_brl(struct file_id id,
385 : struct server_id pid,
386 : enum brl_type lock_type,
387 : enum brl_flavour lock_flav,
388 : br_off start,
389 : br_off size,
390 : void *private_data)
391 : {
392 : unsigned int i;
393 : static const struct {
394 : enum brl_type lock_type;
395 : const char *desc;
396 : } lock_types[] = {
397 : { READ_LOCK, "R" },
398 : { WRITE_LOCK, "W" },
399 : { UNLOCK_LOCK, "U" }
400 : };
401 0 : const char *desc="X";
402 0 : const char *sharepath = "";
403 0 : char *fname = NULL;
404 : struct share_mode_lock *share_mode;
405 : struct server_id_buf tmp;
406 : struct file_id_buf ftmp;
407 0 : struct traverse_state *state = (struct traverse_state *)private_data;
408 :
409 0 : share_mode = fetch_share_mode_unlocked(NULL, id);
410 0 : if (share_mode) {
411 0 : fname = share_mode_filename(NULL, share_mode);
412 0 : sharepath = share_mode_servicepath(share_mode);
413 : } else {
414 0 : fname = talloc_strdup(NULL, "");
415 0 : if (fname == NULL) {
416 0 : return;
417 : }
418 : }
419 :
420 0 : for (i=0;i<ARRAY_SIZE(lock_types);i++) {
421 0 : if (lock_type == lock_types[i].lock_type) {
422 0 : desc = lock_types[i].desc;
423 : }
424 : }
425 :
426 0 : if (!state->json_output) {
427 0 : print_brl_stdout(state,
428 : server_id_str_buf(pid, &tmp),
429 : file_id_str_buf(id, &ftmp),
430 : desc,
431 : (intmax_t)start,
432 : (intmax_t)size,
433 : sharepath,
434 : fname);
435 : } else {
436 0 : print_brl_json(state,
437 : pid,
438 : id,
439 : desc,
440 : lock_flav,
441 : (intmax_t)start,
442 : (intmax_t)size,
443 : sharepath,
444 : fname);
445 :
446 : }
447 :
448 0 : TALLOC_FREE(fname);
449 0 : TALLOC_FREE(share_mode);
450 : }
451 :
452 12 : static const char *session_dialect_str(uint16_t dialect)
453 : {
454 : static fstring unknown_dialect;
455 :
456 12 : switch(dialect){
457 0 : case SMB2_DIALECT_REVISION_000:
458 0 : return "NT1";
459 0 : case SMB2_DIALECT_REVISION_202:
460 0 : return "SMB2_02";
461 0 : case SMB2_DIALECT_REVISION_210:
462 0 : return "SMB2_10";
463 0 : case SMB2_DIALECT_REVISION_222:
464 0 : return "SMB2_22";
465 0 : case SMB2_DIALECT_REVISION_224:
466 0 : return "SMB2_24";
467 0 : case SMB3_DIALECT_REVISION_300:
468 0 : return "SMB3_00";
469 0 : case SMB3_DIALECT_REVISION_302:
470 0 : return "SMB3_02";
471 0 : case SMB3_DIALECT_REVISION_310:
472 0 : return "SMB3_10";
473 12 : case SMB3_DIALECT_REVISION_311:
474 12 : return "SMB3_11";
475 : }
476 :
477 0 : fstr_sprintf(unknown_dialect, "Unknown (0x%04x)", dialect);
478 0 : return unknown_dialect;
479 : }
480 :
481 6 : static int traverse_connections_stdout(struct traverse_state *state,
482 : const char *servicename,
483 : char *server_id,
484 : const char *machine,
485 : const char *timestr,
486 : const char *encryption_cipher,
487 : enum crypto_degree encryption_degree,
488 : const char *signing_cipher,
489 : enum crypto_degree signing_degree)
490 : {
491 : fstring encryption;
492 : fstring signing;
493 :
494 6 : if (encryption_degree == CRYPTO_DEGREE_FULL) {
495 0 : fstr_sprintf(encryption, "%s", encryption_cipher);
496 6 : } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) {
497 0 : fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher);
498 6 : } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) {
499 0 : fstr_sprintf(encryption, "partial(%s)", encryption_cipher);
500 : } else {
501 6 : fstr_sprintf(encryption, "-");
502 : }
503 6 : if (signing_degree == CRYPTO_DEGREE_FULL) {
504 0 : fstr_sprintf(signing, "%s", signing_cipher);
505 6 : } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) {
506 0 : fstr_sprintf(signing, "anonymous(%s)", signing_cipher);
507 6 : } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) {
508 0 : fstr_sprintf(signing, "partial(%s)", signing_cipher);
509 : } else {
510 6 : fstr_sprintf(signing, "-");
511 : }
512 :
513 6 : d_printf("%-12s %-7s %-13s %-32s %-12s %-12s\n",
514 : servicename, server_id, machine, timestr, encryption, signing);
515 :
516 6 : return 0;
517 : }
518 :
519 10 : static int prepare_connections(struct traverse_state *state)
520 : {
521 10 : if (!state->json_output) {
522 : /* always print header line */
523 6 : d_printf("\n%-12s %-7s %-13s %-32s %-12s %-12s\n", "Service", "pid", "Machine", "Connected at", "Encryption", "Signing");
524 6 : d_printf("---------------------------------------------------------------------------------------------\n");
525 : } else {
526 4 : add_section_to_json(state, "tcons");
527 : }
528 10 : return 0;
529 : }
530 :
531 10 : static int traverse_connections(const struct connections_data *crec,
532 : void *private_data)
533 : {
534 : struct server_id_buf tmp;
535 10 : char *timestr = NULL;
536 10 : int result = 0;
537 10 : const char *encryption = "-";
538 10 : enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE;
539 10 : const char *signing = "-";
540 10 : enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE;
541 10 : struct traverse_state *state = (struct traverse_state *)private_data;
542 :
543 10 : TALLOC_CTX *tmp_ctx = talloc_stackframe();
544 10 : if (tmp_ctx == NULL) {
545 0 : return -1;
546 : }
547 :
548 10 : if (crec->cnum == TID_FIELD_INVALID) {
549 0 : TALLOC_FREE(tmp_ctx);
550 0 : return 0;
551 : }
552 :
553 10 : if (do_checks &&
554 10 : (!process_exists(crec->pid) || !Ucrit_checkUid(crec->uid))) {
555 0 : TALLOC_FREE(tmp_ctx);
556 0 : return 0;
557 : }
558 :
559 10 : timestr = timestring(tmp_ctx, nt_time_to_unix(crec->start));
560 10 : if (timestr == NULL) {
561 0 : TALLOC_FREE(tmp_ctx);
562 0 : return -1;
563 : }
564 :
565 20 : if (smbXsrv_is_encrypted(crec->encryption_flags) ||
566 10 : smbXsrv_is_partially_encrypted(crec->encryption_flags))
567 : {
568 0 : switch (crec->cipher) {
569 0 : case SMB_ENCRYPTION_GSSAPI:
570 0 : encryption = "GSSAPI";
571 0 : break;
572 0 : case SMB2_ENCRYPTION_AES128_CCM:
573 0 : encryption = "AES-128-CCM";
574 0 : break;
575 0 : case SMB2_ENCRYPTION_AES128_GCM:
576 0 : encryption = "AES-128-GCM";
577 0 : break;
578 0 : case SMB2_ENCRYPTION_AES256_CCM:
579 0 : encryption = "AES-256-CCM";
580 0 : break;
581 0 : case SMB2_ENCRYPTION_AES256_GCM:
582 0 : encryption = "AES-256-GCM";
583 0 : break;
584 0 : default:
585 0 : encryption = "???";
586 0 : break;
587 : }
588 0 : if (smbXsrv_is_encrypted(crec->encryption_flags)) {
589 0 : encryption_degree = CRYPTO_DEGREE_FULL;
590 0 : } else if (smbXsrv_is_partially_encrypted(crec->encryption_flags)) {
591 0 : encryption_degree = CRYPTO_DEGREE_PARTIAL;
592 : }
593 0 : if (encryption_degree != CRYPTO_DEGREE_NONE &&
594 0 : !crec->authenticated)
595 : {
596 0 : encryption_degree = CRYPTO_DEGREE_ANONYMOUS;
597 : }
598 : }
599 :
600 20 : if (smbXsrv_is_signed(crec->signing_flags) ||
601 10 : smbXsrv_is_partially_signed(crec->signing_flags))
602 : {
603 0 : switch (crec->signing) {
604 0 : case SMB2_SIGNING_MD5_SMB1:
605 0 : signing = "HMAC-MD5";
606 0 : break;
607 0 : case SMB2_SIGNING_HMAC_SHA256:
608 0 : signing = "HMAC-SHA256";
609 0 : break;
610 0 : case SMB2_SIGNING_AES128_CMAC:
611 0 : signing = "AES-128-CMAC";
612 0 : break;
613 0 : case SMB2_SIGNING_AES128_GMAC:
614 0 : signing = "AES-128-GMAC";
615 0 : break;
616 0 : default:
617 0 : signing = "???";
618 0 : break;
619 : }
620 0 : if (smbXsrv_is_signed(crec->signing_flags)) {
621 0 : signing_degree = CRYPTO_DEGREE_FULL;
622 0 : } else if (smbXsrv_is_partially_signed(crec->signing_flags)) {
623 0 : signing_degree = CRYPTO_DEGREE_PARTIAL;
624 : }
625 0 : if (signing_degree != CRYPTO_DEGREE_NONE &&
626 0 : !crec->authenticated)
627 : {
628 0 : signing_degree = CRYPTO_DEGREE_ANONYMOUS;
629 : }
630 : }
631 :
632 10 : if (!state->json_output) {
633 12 : result = traverse_connections_stdout(state,
634 6 : crec->servicename,
635 : server_id_str_buf(crec->pid, &tmp),
636 6 : crec->machine,
637 : timestr,
638 : encryption,
639 : encryption_degree,
640 : signing,
641 : signing_degree);
642 : } else {
643 4 : result = traverse_connections_json(state,
644 : crec,
645 : encryption,
646 : encryption_degree,
647 : signing,
648 : signing_degree);
649 : }
650 :
651 10 : TALLOC_FREE(timestr);
652 10 : TALLOC_FREE(tmp_ctx);
653 :
654 10 : return result;
655 : }
656 :
657 8 : static int traverse_sessionid_stdout(struct traverse_state *state,
658 : char *server_id,
659 : char *uid_gid_str,
660 : char *machine_hostname,
661 : const char *dialect,
662 : const char *encryption_cipher,
663 : enum crypto_degree encryption_degree,
664 : const char *signing_cipher,
665 : enum crypto_degree signing_degree)
666 : {
667 : fstring encryption;
668 : fstring signing;
669 :
670 8 : if (encryption_degree == CRYPTO_DEGREE_FULL) {
671 0 : fstr_sprintf(encryption, "%s", encryption_cipher);
672 8 : } else if (encryption_degree == CRYPTO_DEGREE_ANONYMOUS) {
673 0 : fstr_sprintf(encryption, "anonymous(%s)", encryption_cipher);
674 8 : } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) {
675 0 : fstr_sprintf(encryption, "partial(%s)", encryption_cipher);
676 : } else {
677 8 : fstr_sprintf(encryption, "-");
678 : }
679 8 : if (signing_degree == CRYPTO_DEGREE_FULL) {
680 0 : fstr_sprintf(signing, "%s", signing_cipher);
681 8 : } else if (signing_degree == CRYPTO_DEGREE_ANONYMOUS) {
682 0 : fstr_sprintf(signing, "anonymous(%s)", signing_cipher);
683 8 : } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) {
684 7 : fstr_sprintf(signing, "partial(%s)", signing_cipher);
685 : } else {
686 1 : fstr_sprintf(signing, "-");
687 : }
688 :
689 8 : d_printf("%-7s %-25s %-41s %-17s %-20s %-21s\n",
690 : server_id, uid_gid_str, machine_hostname, dialect, encryption,
691 : signing);
692 :
693 8 : return 0;
694 : }
695 :
696 12 : static int prepare_sessionid(struct traverse_state *state)
697 : {
698 12 : if (!state->json_output) {
699 : /* always print header line */
700 8 : d_printf("\nSamba version %s\n",samba_version_string());
701 8 : d_printf("%-7s %-12s %-12s %-41s %-17s %-20s %-21s\n", "PID", "Username", "Group", "Machine", "Protocol Version", "Encryption", "Signing");
702 8 : d_printf("----------------------------------------------------------------------------------------------------------------------------------------\n");
703 : } else {
704 4 : add_section_to_json(state, "sessions");
705 : }
706 12 : return 0;
707 :
708 : }
709 :
710 12 : static int traverse_sessionid(const char *key, struct sessionid *session,
711 : void *private_data)
712 : {
713 : fstring uid_gid_str;
714 : fstring uid_str;
715 : fstring gid_str;
716 : struct server_id_buf tmp;
717 12 : char *machine_hostname = NULL;
718 12 : int result = 0;
719 12 : const char *encryption = "-";
720 12 : enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE;
721 12 : const char *signing = "-";
722 12 : enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE;
723 12 : struct traverse_state *state = (struct traverse_state *)private_data;
724 :
725 12 : TALLOC_CTX *tmp_ctx = talloc_stackframe();
726 12 : if (tmp_ctx == NULL) {
727 0 : return -1;
728 : }
729 :
730 12 : if (do_checks &&
731 24 : (!process_exists(session->pid) ||
732 12 : !Ucrit_checkUid(session->uid))) {
733 0 : TALLOC_FREE(tmp_ctx);
734 0 : return 0;
735 : }
736 :
737 12 : Ucrit_addPid(session->pid);
738 :
739 12 : if (numeric_only) {
740 0 : fstr_sprintf(gid_str, "%u", (unsigned int)session->gid);
741 0 : fstr_sprintf(uid_str, "%u", (unsigned int)session->uid);
742 0 : fstr_sprintf(uid_gid_str, "%-12u %-12u",
743 0 : (unsigned int)session->uid,
744 0 : (unsigned int)session->gid);
745 : } else {
746 12 : if (session->uid == -1 && session->gid == -1) {
747 : /*
748 : * The session is not fully authenticated yet.
749 : */
750 0 : fstrcpy(uid_gid_str, "(auth in progress)");
751 0 : fstrcpy(gid_str, "(auth in progress)");
752 0 : fstrcpy(uid_str, "(auth in progress)");
753 : } else {
754 : /*
755 : * In theory it should not happen that one of
756 : * session->uid and session->gid is valid (ie != -1)
757 : * while the other is not (ie = -1), so we a check for
758 : * that case that bails out would be reasonable.
759 : */
760 12 : const char *uid_name = "-1";
761 12 : const char *gid_name = "-1";
762 :
763 12 : if (session->uid != -1) {
764 12 : uid_name = uidtoname(session->uid);
765 12 : if (uid_name == NULL) {
766 0 : TALLOC_FREE(tmp_ctx);
767 0 : return -1;
768 : }
769 : }
770 12 : if (session->gid != -1) {
771 12 : gid_name = gidtoname(session->gid);
772 12 : if (gid_name == NULL) {
773 0 : TALLOC_FREE(tmp_ctx);
774 0 : return -1;
775 : }
776 : }
777 12 : fstr_sprintf(gid_str, "%s", gid_name);
778 12 : fstr_sprintf(uid_str, "%s", uid_name);
779 12 : fstr_sprintf(uid_gid_str, "%-12s %-12s",
780 : uid_name, gid_name);
781 : }
782 : }
783 :
784 12 : machine_hostname = talloc_asprintf(tmp_ctx, "%s (%s)",
785 12 : session->remote_machine,
786 12 : session->hostname);
787 12 : if (machine_hostname == NULL) {
788 0 : TALLOC_FREE(tmp_ctx);
789 0 : return -1;
790 : }
791 :
792 24 : if (smbXsrv_is_encrypted(session->encryption_flags) ||
793 12 : smbXsrv_is_partially_encrypted(session->encryption_flags)) {
794 0 : switch (session->cipher) {
795 0 : case SMB2_ENCRYPTION_AES128_CCM:
796 0 : encryption = "AES-128-CCM";
797 0 : break;
798 0 : case SMB2_ENCRYPTION_AES128_GCM:
799 0 : encryption = "AES-128-GCM";
800 0 : break;
801 0 : case SMB2_ENCRYPTION_AES256_CCM:
802 0 : encryption = "AES-256-CCM";
803 0 : break;
804 0 : case SMB2_ENCRYPTION_AES256_GCM:
805 0 : encryption = "AES-256-GCM";
806 0 : break;
807 0 : default:
808 0 : encryption = "???";
809 0 : result = -1;
810 0 : break;
811 : }
812 0 : if (smbXsrv_is_encrypted(session->encryption_flags)) {
813 0 : encryption_degree = CRYPTO_DEGREE_FULL;
814 0 : } else if (smbXsrv_is_partially_encrypted(session->encryption_flags)) {
815 0 : encryption_degree = CRYPTO_DEGREE_PARTIAL;
816 : }
817 0 : if (encryption_degree != CRYPTO_DEGREE_NONE &&
818 0 : !session->authenticated)
819 : {
820 0 : encryption_degree = CRYPTO_DEGREE_ANONYMOUS;
821 : }
822 : }
823 :
824 24 : if (smbXsrv_is_signed(session->signing_flags) ||
825 12 : smbXsrv_is_partially_signed(session->signing_flags)) {
826 11 : switch (session->signing) {
827 0 : case SMB2_SIGNING_MD5_SMB1:
828 0 : signing = "HMAC-MD5";
829 0 : break;
830 0 : case SMB2_SIGNING_HMAC_SHA256:
831 0 : signing = "HMAC-SHA256";
832 0 : break;
833 0 : case SMB2_SIGNING_AES128_CMAC:
834 0 : signing = "AES-128-CMAC";
835 0 : break;
836 11 : case SMB2_SIGNING_AES128_GMAC:
837 11 : signing = "AES-128-GMAC";
838 11 : break;
839 0 : default:
840 0 : signing = "???";
841 0 : result = -1;
842 0 : break;
843 : }
844 11 : if (smbXsrv_is_signed(session->signing_flags)) {
845 0 : signing_degree = CRYPTO_DEGREE_FULL;
846 11 : } else if (smbXsrv_is_partially_signed(session->signing_flags)) {
847 11 : signing_degree = CRYPTO_DEGREE_PARTIAL;
848 : }
849 11 : if (signing_degree != CRYPTO_DEGREE_NONE &&
850 11 : !session->authenticated)
851 : {
852 0 : signing_degree = CRYPTO_DEGREE_ANONYMOUS;
853 : }
854 : }
855 :
856 :
857 12 : if (!state->json_output) {
858 8 : traverse_sessionid_stdout(state,
859 : server_id_str_buf(session->pid, &tmp),
860 : uid_gid_str,
861 : machine_hostname,
862 8 : session_dialect_str(session->connection_dialect),
863 : encryption,
864 : encryption_degree,
865 : signing,
866 : signing_degree);
867 : } else {
868 4 : result = traverse_sessionid_json(state,
869 : session,
870 : uid_str,
871 : gid_str,
872 : encryption,
873 : encryption_degree,
874 : signing,
875 : signing_degree,
876 4 : session_dialect_str(session->connection_dialect));
877 : }
878 :
879 12 : TALLOC_FREE(machine_hostname);
880 12 : TALLOC_FREE(tmp_ctx);
881 :
882 12 : return result;
883 : }
884 :
885 :
886 0 : static bool print_notify_rec_stdout(struct traverse_state *state,
887 : const char *path,
888 : char *server_id_str,
889 : unsigned filter,
890 : unsigned subdir_filter)
891 : {
892 0 : d_printf("%s\\%s\\%x\\%x\n", path, server_id_str,
893 : filter, subdir_filter);
894 :
895 0 : return true;
896 : }
897 :
898 2 : static int prepare_notify(struct traverse_state *state)
899 : {
900 2 : if (!state->json_output) {
901 : /* don't print header line */
902 : } else {
903 2 : add_section_to_json(state, "notifies");
904 : }
905 2 : return 0;
906 : }
907 :
908 0 : static bool print_notify_rec(const char *path, struct server_id server,
909 : const struct notify_instance *instance,
910 : void *private_data)
911 : {
912 : struct server_id_buf idbuf;
913 0 : struct traverse_state *state = (struct traverse_state *)private_data;
914 : bool result;
915 :
916 0 : if (!state->json_output) {
917 0 : result = print_notify_rec_stdout(state,
918 : path,
919 : server_id_str_buf(server, &idbuf),
920 0 : (unsigned)instance->filter,
921 0 : (unsigned)instance->subdir_filter);
922 :
923 : } else {
924 0 : result = print_notify_rec_json(state,
925 : instance,
926 : server,
927 : path);
928 : }
929 :
930 0 : return result;
931 : }
932 :
933 : enum {
934 : OPT_RESOLVE_UIDS = 1000,
935 : };
936 :
937 18 : int main(int argc, const char *argv[])
938 : {
939 : int c;
940 18 : int profile_only = 0;
941 : bool show_processes, show_locks, show_shares;
942 18 : bool show_notify = false;
943 18 : poptContext pc = NULL;
944 18 : struct traverse_state state = {0};
945 54 : struct poptOption long_options[] = {
946 : POPT_AUTOHELP
947 : {
948 : .longName = "processes",
949 : .shortName = 'p',
950 : .argInfo = POPT_ARG_NONE,
951 : .arg = NULL,
952 : .val = 'p',
953 : .descrip = "Show processes only",
954 : },
955 : {
956 : .longName = "verbose",
957 : .shortName = 'v',
958 : .argInfo = POPT_ARG_NONE,
959 : .arg = NULL,
960 : .val = 'v',
961 : .descrip = "Be verbose",
962 : },
963 : {
964 : .longName = "locks",
965 : .shortName = 'L',
966 : .argInfo = POPT_ARG_NONE,
967 : .arg = NULL,
968 : .val = 'L',
969 : .descrip = "Show locks only",
970 : },
971 : {
972 : .longName = "shares",
973 : .shortName = 'S',
974 : .argInfo = POPT_ARG_NONE,
975 : .arg = NULL,
976 : .val = 'S',
977 : .descrip = "Show shares only",
978 : },
979 : {
980 : .longName = "notify",
981 : .shortName = 'N',
982 : .argInfo = POPT_ARG_NONE,
983 : .arg = NULL,
984 : .val = 'N',
985 : .descrip = "Show notifies",
986 : },
987 : {
988 : .longName = "user",
989 : .shortName = 'u',
990 : .argInfo = POPT_ARG_STRING,
991 : .arg = &username,
992 : .val = 'u',
993 : .descrip = "Switch to user",
994 : },
995 : {
996 : .longName = "brief",
997 : .shortName = 'b',
998 : .argInfo = POPT_ARG_NONE,
999 : .arg = NULL,
1000 : .val = 'b',
1001 : .descrip = "Be brief",
1002 : },
1003 : {
1004 : .longName = "profile",
1005 : .shortName = 'P',
1006 : .argInfo = POPT_ARG_NONE,
1007 : .arg = NULL,
1008 : .val = 'P',
1009 : .descrip = "Do profiling",
1010 : },
1011 : {
1012 : .longName = "profile-rates",
1013 : .shortName = 'R',
1014 : .argInfo = POPT_ARG_NONE,
1015 : .arg = NULL,
1016 : .val = 'R',
1017 : .descrip = "Show call rates",
1018 : },
1019 : {
1020 : .longName = "byterange",
1021 : .shortName = 'B',
1022 : .argInfo = POPT_ARG_NONE,
1023 : .arg = NULL,
1024 : .val = 'B',
1025 : .descrip = "Include byte range locks"
1026 : },
1027 : {
1028 : .longName = "numeric",
1029 : .shortName = 'n',
1030 : .argInfo = POPT_ARG_NONE,
1031 : .arg = NULL,
1032 : .val = 'n',
1033 : .descrip = "Numeric uid/gid"
1034 : },
1035 : {
1036 : .longName = "json",
1037 : .shortName = 'j',
1038 : .argInfo = POPT_ARG_NONE,
1039 : .arg = NULL,
1040 : .val = 'j',
1041 : .descrip = "JSON output"
1042 : },
1043 : {
1044 : .longName = "fast",
1045 : .shortName = 'f',
1046 : .argInfo = POPT_ARG_NONE,
1047 : .arg = NULL,
1048 : .val = 'f',
1049 : .descrip = "Skip checks if processes still exist"
1050 : },
1051 : {
1052 : .longName = "resolve-uids",
1053 : .shortName = 0,
1054 : .argInfo = POPT_ARG_NONE,
1055 : .arg = NULL,
1056 : .val = OPT_RESOLVE_UIDS,
1057 : .descrip = "Try to resolve UIDs to usernames"
1058 : },
1059 18 : POPT_COMMON_SAMBA
1060 18 : POPT_COMMON_VERSION
1061 : POPT_TABLEEND
1062 : };
1063 18 : TALLOC_CTX *frame = talloc_stackframe();
1064 18 : int ret = 0;
1065 18 : struct messaging_context *msg_ctx = NULL;
1066 : char *db_path;
1067 : bool ok;
1068 18 : struct loadparm_context *lp_ctx = NULL;
1069 :
1070 18 : state.first = true;
1071 18 : state.json_output = false;
1072 18 : state.resolve_uids = false;
1073 :
1074 18 : smb_init_locale();
1075 :
1076 18 : ok = samba_cmdline_init(frame,
1077 : SAMBA_CMDLINE_CONFIG_CLIENT,
1078 : false /* require_smbconf */);
1079 18 : if (!ok) {
1080 0 : DBG_ERR("Failed to init cmdline parser!\n");
1081 0 : TALLOC_FREE(frame);
1082 0 : exit(1);
1083 : }
1084 18 : lp_ctx = samba_cmdline_get_lp_ctx();
1085 18 : lpcfg_set_cmdline(lp_ctx, "log level", "0");
1086 :
1087 18 : pc = samba_popt_get_context(getprogname(),
1088 : argc,
1089 : argv,
1090 : long_options,
1091 : POPT_CONTEXT_KEEP_FIRST);
1092 18 : if (pc == NULL) {
1093 0 : DBG_ERR("Failed to setup popt context!\n");
1094 0 : TALLOC_FREE(frame);
1095 0 : exit(1);
1096 : }
1097 :
1098 40 : while ((c = poptGetNextOpt(pc)) != -1) {
1099 22 : switch (c) {
1100 4 : case 'p':
1101 4 : processes_only = true;
1102 4 : break;
1103 2 : case 'v':
1104 2 : verbose = true;
1105 2 : break;
1106 2 : case 'L':
1107 2 : locks_only = true;
1108 2 : break;
1109 2 : case 'S':
1110 2 : shares_only = true;
1111 2 : break;
1112 2 : case 'N':
1113 2 : show_notify = true;
1114 2 : break;
1115 0 : case 'b':
1116 0 : brief = true;
1117 0 : break;
1118 0 : case 'u':
1119 0 : Ucrit_addUid(nametouid(poptGetOptArg(pc)));
1120 0 : break;
1121 2 : case 'P':
1122 : case 'R':
1123 2 : profile_only = c;
1124 2 : break;
1125 2 : case 'B':
1126 2 : show_brl = true;
1127 2 : break;
1128 0 : case 'n':
1129 0 : numeric_only = true;
1130 0 : break;
1131 6 : case 'j':
1132 6 : state.json_output = true;
1133 6 : break;
1134 0 : case 'f':
1135 0 : do_checks = false;
1136 0 : break;
1137 0 : case OPT_RESOLVE_UIDS:
1138 0 : state.resolve_uids = true;
1139 0 : break;
1140 0 : case POPT_ERROR_BADOPT:
1141 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
1142 : poptBadOption(pc, 0), poptStrerror(c));
1143 0 : poptPrintUsage(pc, stderr, 0);
1144 0 : exit(1);
1145 : }
1146 : }
1147 :
1148 18 : sec_init();
1149 :
1150 : #ifdef HAVE_JANSSON
1151 18 : state.root_json = json_new_object();
1152 18 : if (!json_is_invalid(&state.root_json)) {
1153 18 : add_general_information_to_json(&state);
1154 : }
1155 : #else /* HAVE_JANSSON */
1156 : if (state.json_output) {
1157 : fprintf(stderr, "JSON support not available, please install lib Jansson\n");
1158 : goto done;
1159 : }
1160 : #endif /* HAVE_JANSSON */
1161 :
1162 18 : if (getuid() != geteuid()) {
1163 0 : fprintf(stderr, "smbstatus should not be run setuid\n");
1164 0 : ret = 1;
1165 0 : goto done;
1166 : }
1167 :
1168 18 : if (getuid() != 0) {
1169 0 : fprintf(stderr, "smbstatus only works as root!\n");
1170 0 : ret = 1;
1171 0 : goto done;
1172 : }
1173 :
1174 : /* setup the flags based on the possible combincations */
1175 :
1176 18 : show_processes = !(shares_only || locks_only || profile_only) || processes_only;
1177 18 : show_locks = !(shares_only || processes_only || profile_only) || locks_only;
1178 18 : show_shares = !(processes_only || locks_only || profile_only) || shares_only;
1179 :
1180 18 : if ( username )
1181 0 : Ucrit_addUid( nametouid(username) );
1182 :
1183 18 : if (verbose && !state.json_output) {
1184 0 : d_printf("using configfile = %s\n", get_dyn_CONFIGFILE());
1185 : }
1186 :
1187 18 : msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
1188 18 : if (msg_ctx == NULL) {
1189 0 : fprintf(stderr, "Could not initialize messaging, not root?\n");
1190 0 : ret = -1;
1191 0 : goto done;
1192 : }
1193 :
1194 18 : switch (profile_only) {
1195 2 : case 'P':
1196 : /* Dump profile data */
1197 2 : ok = status_profile_dump(verbose, &state);
1198 2 : ret = ok ? 0 : 1;
1199 2 : goto done;
1200 0 : case 'R':
1201 : /* Continuously display rate-converted data */
1202 0 : if (!state.json_output) {
1203 0 : ok = status_profile_rates(verbose);
1204 0 : ret = ok ? 0 : 1;
1205 : } else {
1206 0 : fprintf(stderr, "Call rates not available in a json output.\n");
1207 0 : ret = 1;
1208 : }
1209 0 : goto done;
1210 16 : default:
1211 16 : break;
1212 : }
1213 :
1214 16 : if ( show_processes ) {
1215 12 : prepare_sessionid(&state);
1216 12 : sessionid_traverse_read(traverse_sessionid, &state);
1217 :
1218 12 : if (processes_only) {
1219 4 : goto done;
1220 : }
1221 : }
1222 :
1223 12 : if ( show_shares ) {
1224 10 : if (brief) {
1225 0 : goto done;
1226 : }
1227 10 : prepare_connections(&state);
1228 10 : connections_forall_read(traverse_connections, &state);
1229 :
1230 10 : if (!state.json_output) {
1231 6 : d_printf("\n");
1232 : }
1233 :
1234 10 : if ( shares_only ) {
1235 2 : goto done;
1236 : }
1237 : }
1238 :
1239 10 : if ( show_locks ) {
1240 : int result;
1241 : struct db_context *db;
1242 :
1243 10 : db_path = lock_path(talloc_tos(), "locking.tdb");
1244 10 : if (db_path == NULL) {
1245 0 : fprintf(stderr, "Out of memory - exiting\n");
1246 0 : ret = -1;
1247 0 : goto done;
1248 : }
1249 :
1250 10 : db = db_open(NULL, db_path, 0,
1251 : TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDONLY, 0,
1252 : DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
1253 :
1254 10 : if (!db) {
1255 0 : fprintf(stderr, "%s not initialised\n", db_path);
1256 0 : fprintf(stderr, "This is normal if an SMB client has never "
1257 : "connected to your server.\n");
1258 0 : TALLOC_FREE(db_path);
1259 0 : ret = 0;
1260 0 : goto done;
1261 : } else {
1262 10 : TALLOC_FREE(db);
1263 10 : TALLOC_FREE(db_path);
1264 : }
1265 :
1266 10 : if (!locking_init_readonly()) {
1267 0 : fprintf(stderr, "Can't initialise locking module - exiting\n");
1268 0 : ret = 1;
1269 0 : goto done;
1270 : }
1271 :
1272 10 : prepare_share_mode(&state);
1273 10 : result = share_entry_forall(print_share_mode, &state);
1274 :
1275 10 : if (result == 0 && !state.json_output) {
1276 0 : fprintf(stderr, "No locked files\n");
1277 10 : } else if (result < 0 && !state.json_output) {
1278 0 : fprintf(stderr, "locked file list truncated\n");
1279 : }
1280 :
1281 10 : if (!state.json_output) {
1282 6 : d_printf("\n");
1283 : }
1284 :
1285 10 : if (show_brl) {
1286 2 : prepare_brl(&state);
1287 2 : brl_forall(print_brl, &state);
1288 : }
1289 :
1290 10 : locking_end();
1291 : }
1292 :
1293 10 : if (show_notify) {
1294 2 : prepare_notify(&state);
1295 2 : notify_walk(msg_ctx, print_notify_rec, &state);
1296 : }
1297 :
1298 8 : done:
1299 18 : cmdline_messaging_context_free();
1300 18 : poptFreeContext(pc);
1301 : #ifdef HAVE_JANSSON
1302 18 : if (state.json_output) {
1303 6 : d_printf("%s\n", json_to_string(frame, &state.root_json));
1304 : }
1305 18 : json_free(&state.root_json);
1306 : #endif /* HAVE_JANSSON */
1307 18 : TALLOC_FREE(frame);
1308 18 : return ret;
1309 : }
|