s3: smbcontrol to notify smbd about idmap changes
authorGregor Beck <gbeck@sernet.de>
Thu, 3 Feb 2011 15:30:29 +0000 (16:30 +0100)
committerMichael Adam <obnox@samba.org>
Mon, 28 Feb 2011 13:07:23 +0000 (14:07 +0100)
Autobuild-User: Michael Adam <obnox@samba.org>
Autobuild-Date: Mon Feb 28 14:07:23 CET 2011 on sn-devel-104

source3/Makefile.in
source3/include/proto.h
source3/librpc/idl/messaging.idl
source3/smbd/msg_idmap.c [new file with mode: 0644]
source3/smbd/server.c
source3/utils/smbcontrol.c

index 2f3575f948ff4d163e118cae8440897326dc24c0..b7a7a7858b9b9cb968929577478b71563cfefe25 100644 (file)
@@ -868,7 +868,7 @@ AUTH_OBJ = auth/auth.o @AUTH_STATIC@ auth/auth_util.o auth/token_util.o \
 
 MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_hash2.o
 
-SMBD_OBJ_MAIN = smbd/server.o smbd/server_exit.o
+SMBD_OBJ_MAIN = smbd/server.o smbd/server_exit.o smbd/msg_idmap.o
 
 BUILDOPT_OBJ = smbd/build_options.o
 
index 43426795de2f9425e010389c72bfdbce2d1d4430..cf58cdf99e9a0e57f017300b41059186663b0366 100644 (file)
@@ -5457,6 +5457,10 @@ struct AvahiPoll *tevent_avahi_poll(TALLOC_CTX *mem_ctx,
 void *avahi_start_register(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
                           uint16_t port);
 
+/* The following definitions come from smbd/msg_idmap.c */
+
+void msg_idmap_register_msgs(struct messaging_context *ctx);
+
 /* Misc protos */
 
 struct fncall_context *fncall_context_init(TALLOC_CTX *mem_ctx,
index 36f064f23de4eda6d46135168c53534d4a50f19e..8618d53c9a27234d1f0e38215f2ca84915c9d236 100644 (file)
@@ -35,6 +35,10 @@ interface messaging
                MSG_REQ_DMALLOC_LOG_CHANGED     = 0x000C,
                MSG_SHUTDOWN                    = 0x000D,
 
+               MSG_IDMAP_FLUSH                 = 0x000E,
+               MSG_IDMAP_DELETE                = 0x000F,
+               MSG_IDMAP_KILL                  = 0x0010,
+
                /* nmbd messages */
                MSG_FORCE_ELECTION              = 0x0101,
                MSG_WINS_NEW_ENTRY              = 0x0102,
diff --git a/source3/smbd/msg_idmap.c b/source3/smbd/msg_idmap.c
new file mode 100644 (file)
index 0000000..9e2424d
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief  Notify smbd about idmap changes
+ * @file   msg_idmap.c
+ * @author Gregor Beck <gb@sernet.de>
+ * @date   Feb 2011
+ *
+ */
+
+#include "includes.h"
+#include "memcache.h"
+#include "globals.h"
+#include "../libcli/security/dom_sid.h"
+#include "../librpc/gen_ndr/messaging.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "idmap_cache.h"
+
+struct id {
+       union {
+               uid_t uid;
+               gid_t gid;
+               struct dom_sid sid;
+       };
+       enum {UID, GID, SID} type;
+};
+
+static bool parse_id(const char* str, struct id* id)
+{
+       struct dom_sid sid;
+       unsigned long ul;
+       char c, trash;
+
+       if (sscanf(str, "%cID %lu%c", &c, &ul, &trash) == 2) {
+               switch(c) {
+               case 'G':
+                       id->gid = ul;
+                       id->type = GID;
+                       return true;
+               case 'U':
+                       id->uid = ul;
+                       id->type = UID;
+                       return true;
+               default:
+                       break;
+               }
+       } else if (string_to_sid(&sid, str)) {
+               id->sid = sid;
+               id->type = SID;
+               return true;
+       }
+       return false;
+}
+
+static bool uid_in_use(const struct user_struct* user, uid_t uid)
+{
+       while (user) {
+               if (user->session_info && (user->session_info->utok.uid == uid)) {
+                       return true;
+               }
+               user = user->next;
+       }
+       return false;
+}
+
+static bool gid_in_use(const struct user_struct* user, gid_t gid)
+{
+       while (user) {
+               if (user->session_info != NULL) {
+                       int i;
+                       struct unix_user_token utok = user->session_info->utok;
+                       if (utok.gid == gid) {
+                               return true;
+                       }
+                       for(i=0; i<utok.ngroups; i++) {
+                               if (utok.groups[i] == gid) {
+                                       return true;
+                               }
+                       }
+               }
+               user = user->next;
+       }
+       return false;
+}
+
+static bool sid_in_use(const struct user_struct* user, const struct dom_sid* psid)
+{
+       uid_t uid;
+       gid_t gid;
+       if (sid_to_gid(psid, &gid)) {
+               return gid_in_use(user, gid);
+       } else if (sid_to_uid(psid, &uid)) {
+               return uid_in_use(user, uid);
+       }
+       return false;
+}
+
+
+static bool id_in_use(const struct user_struct* user, const struct id* id)
+{
+       switch(id->type) {
+       case UID:
+               return uid_in_use(user, id->uid);
+       case GID:
+               return gid_in_use(user, id->gid);
+       case SID:
+               return sid_in_use(user, &id->sid);
+       default:
+               break;
+       }
+       return false;
+}
+
+static void delete_from_cache(const struct id* id)
+{
+       switch(id->type) {
+       case UID:
+               delete_uid_cache(id->uid);
+               idmap_cache_del_uid(id->uid);
+               break;
+       case GID:
+               delete_gid_cache(id->gid);
+               idmap_cache_del_gid(id->gid);
+               break;
+       case SID:
+               delete_sid_cache(&id->sid);
+               idmap_cache_del_sid(&id->sid);
+               break;
+       default:
+               break;
+       }
+}
+
+
+static void message_idmap_flush(struct messaging_context *msg_ctx,
+                               void* private_data,
+                               uint32_t msg_type,
+                               struct server_id server_id,
+                               DATA_BLOB* data)
+{
+       const char* msg = data ? (const char*)data->data : NULL;
+
+       DEBUG(0, ("Foo: idmap flush cache message(0x%.2x): %s\n", msg_type, msg ? msg : "<NULL>"));
+
+       if ((msg == NULL) || (msg[0] == '\0')) {
+               flush_gid_cache();
+               flush_uid_cache();
+       } else if (strncmp(msg, "GID", 3)) {
+               flush_gid_cache();
+       } else if (strncmp(msg, "UID", 3)) {
+               flush_uid_cache();
+       } else {
+               DEBUG(0, ("Invalid argument: %s\n", msg));
+       }
+}
+
+
+static void message_idmap_delete(struct messaging_context *msg_ctx,
+                                void* private_data,
+                                uint32_t msg_type,
+                                struct server_id server_id,
+                                DATA_BLOB* data)
+{
+       const char* msg = (data && data->data) ? (const char*)data->data : "<NULL>";
+       bool do_kill = (msg_type == MSG_IDMAP_KILL);
+       struct user_struct* validated_users = smbd_server_conn->smb1.sessions.validated_users;
+       struct id id;
+
+       DEBUG(0, ("Foo: idmap delete message(0x%.2x): %s\n", msg_type, msg));
+
+       if (!parse_id(msg, &id)) {
+               DEBUG(0, ("Invalid ?ID: %s\n", msg));
+               return;
+       }
+
+       if( do_kill && id_in_use(validated_users, &id) ) {
+               exit_server_cleanly(msg);
+       } else {
+               delete_from_cache(&id);
+       }
+}
+
+void msg_idmap_register_msgs(struct messaging_context *ctx)
+{
+       messaging_register(ctx, NULL, MSG_IDMAP_FLUSH,  message_idmap_flush);
+       messaging_register(ctx, NULL, MSG_IDMAP_DELETE, message_idmap_delete);
+       messaging_register(ctx, NULL, MSG_IDMAP_KILL,   message_idmap_delete);
+}
index 81283f0ec956551fb6f59dd6c2652be348a4639d..dd715e993ad34f6253ef0d7bd23f2984d9d349c7 100644 (file)
@@ -760,6 +760,8 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
        messaging_register(msg_ctx, NULL, MSG_PRINTER_PCAP, smb_pcap_updated);
        brl_register_msgs(msg_ctx);
 
+       msg_idmap_register_msgs(msg_ctx);
+
 #ifdef CLUSTER_SUPPORT
        if (lp_clustering()) {
                ctdbd_register_reconfigure(messaging_ctdbd_connection());
index 378534d87a94fb21f6fac46fbb3d7ab412211eb1..ed7ca596d8ad9fa7e3b7d7d2953833e16e867d75 100644 (file)
@@ -167,6 +167,54 @@ static bool do_debug(struct messaging_context *msg_ctx,
                            strlen(argv[1]) + 1);
 }
 
+
+static bool do_idmap(struct messaging_context *msg_ctx,
+                    const struct server_id pid,
+                    const int argc, const char **argv)
+{
+       static const char* usage = "Usage: "
+               "smbcontrol <dest> idmap <cmd> [arg]\n"
+               "\tcmd:\tflush [gid|uid]\n"
+               "\t\tdelete \"UID <uid>\"|\"GID <gid>\"|<sid>\n"
+               "\t\tkill \"UID <uid>\"|\"GID <gid>\"|<sid>\n";
+       const char* arg = NULL;
+       int arglen = 0;
+       int msg_type;
+
+       switch (argc) {
+       case 2:
+               break;
+       case 3:
+               arg = argv[2];
+               arglen = strlen(arg) + 1;
+               break;
+       default:
+               fprintf(stderr, "%s", usage);
+               return false;
+       }
+
+       if (strcmp(argv[1], "flush") == 0) {
+               msg_type = MSG_IDMAP_FLUSH;
+       }
+       else if (strcmp(argv[1], "delete") == 0) {
+               msg_type = MSG_IDMAP_DELETE;
+       }
+       else if (strcmp(argv[1], "kill") == 0) {
+               msg_type = MSG_IDMAP_KILL;
+       }
+       else if (strcmp(argv[1], "help") == 0) {
+               fprintf(stdout, "%s", usage);
+               return true;
+       }
+       else {
+               fprintf(stderr, "%s", usage);
+               return false;
+       }
+
+       return send_message(msg_ctx, pid, msg_type, arg, arglen);
+}
+
+
 #if defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE)
 
 /* Return the name of a process given it's PID. This will only work on Linux,
@@ -1201,6 +1249,7 @@ static const struct {
        const char *help;       /* Short help text */
 } msg_types[] = {
        { "debug", do_debug, "Set debuglevel"  },
+       { "idmap", do_idmap, "Manipulate idmap cache" },
        { "force-election", do_election,
          "Force a browse election" },
        { "ping", do_ping, "Elicit a response" },