summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/rpc_server/srv_wkssvc_nt.c402
1 files changed, 380 insertions, 22 deletions
diff --git a/source3/rpc_server/srv_wkssvc_nt.c b/source3/rpc_server/srv_wkssvc_nt.c
index 28a36aef3d..b3bfe80200 100644
--- a/source3/rpc_server/srv_wkssvc_nt.c
+++ b/source3/rpc_server/srv_wkssvc_nt.c
@@ -30,33 +30,96 @@
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_RPC_SRV
+struct dom_usr {
+ char *name;
+ char *domain;
+ time_t login_time;
+};
+
#ifdef HAVE_GETUTXENT
#include <utmpx.h>
+struct usrinfo {
+ char *name;
+ struct timeval login_time;
+};
+
+static int usr_info_cmp(const void *p1, const void *p2)
+{
+ const struct usrinfo *usr1 = (const struct usrinfo *)p1;
+ const struct usrinfo *usr2 = (const struct usrinfo *)p2;
+
+ /* Called from qsort to compare two users in a usrinfo_t array for
+ * sorting by login time. Return >0 if usr1 login time was later than
+ * usr2 login time, <0 if it was earlier */
+ return ((usr1->login_time.tv_sec == usr2->login_time.tv_sec)
+ ? usr1->login_time.tv_usec - usr2->login_time.tv_usec
+ : usr1->login_time.tv_sec - usr2->login_time.tv_sec);
+}
+
+/*******************************************************************
+ Get a list of the names of all users logged into this machine
+ ********************************************************************/
+
static char **get_logged_on_userlist(TALLOC_CTX *mem_ctx)
{
- char **users = NULL;
- int num_users = 0;
+ char **users;
+ int i, num_users = 0;
+ struct usrinfo *usr_infos = NULL;
struct utmpx *u;
while ((u = getutxent()) != NULL) {
- char **tmp;
+ struct usrinfo *tmp;
if (u->ut_type != USER_PROCESS) {
continue;
}
- tmp = talloc_realloc(mem_ctx, users, char *, num_users+1);
+ for (i = 0; i < num_users; i++) {
+ /* getutxent can return multiple user entries for the
+ * same user, so ignore any dups */
+ if (strcmp(u->ut_user, usr_infos[i].name) == 0) {
+ break;
+ }
+ }
+ if (i < num_users) {
+ continue;
+ }
+
+ tmp = talloc_realloc(mem_ctx, usr_infos, struct usrinfo,
+ num_users+1);
if (tmp == NULL) {
+ TALLOC_FREE(tmp);
+ endutxent();
return NULL;
}
- users = tmp;
- users[num_users] = talloc_strdup(users, u->ut_user);
- if (users[num_users] == NULL) {
- TALLOC_FREE(users);
+ usr_infos = tmp;
+ usr_infos[num_users].name = talloc_strdup(usr_infos,
+ u->ut_user);
+ if (usr_infos[num_users].name == NULL) {
+ TALLOC_FREE(usr_infos);
+ endutxent();
return NULL;
}
+ usr_infos[num_users].login_time.tv_sec = u->ut_tv.tv_sec;
+ usr_infos[num_users].login_time.tv_usec = u->ut_tv.tv_usec;
num_users += 1;
}
+
+ /* Sort the user list by time, oldest first */
+ if (num_users > 1) {
+ qsort(usr_infos, num_users, sizeof(struct usrinfo),
+ usr_info_cmp);
+ }
+
+ users = (char**)talloc_array(mem_ctx, char*, num_users);
+ if (users) {
+ for (i = 0; i < num_users; i++) {
+ users[i] = talloc_move(users, &usr_infos[i].name);
+ }
+ }
+ TALLOC_FREE(usr_infos);
+ endutxent();
+ errno = 0;
return users;
}
@@ -69,8 +132,129 @@ static char **get_logged_on_userlist(TALLOC_CTX *mem_ctx)
#endif
+static int dom_user_cmp(const void *p1, const void *p2)
+{
+ /* Called from qsort to compare two domain users in a dom_usr_t array
+ * for sorting by login time. Return >0 if usr1 login time was later
+ * than usr2 login time, <0 if it was earlier */
+ const struct dom_usr *usr1 = (const struct dom_usr *)p1;
+ const struct dom_usr *usr2 = (const struct dom_usr *)p2;
+
+ return (usr1->login_time - usr2->login_time);
+}
+
/*******************************************************************
- Fill in the values for the struct wkssvc_NetWkstaInfo100.
+ Get a list of the names of all users of this machine who are
+ logged into the domain.
+
+ This should return a list of the users on this machine who are
+ logged into the domain (i.e. have been authenticated by the domain's
+ password server) but that doesn't fit well with the normal Samba
+ scenario where accesses out to the domain are made through smbclient
+ with each such session individually authenticated. So about the best
+ we can do currently is to list sessions of local users connected to
+ this server, which means that to get themself included in the list a
+ local user must create a session to the local samba server by running:
+ smbclient \\\\localhost\\share
+
+ FIXME: find a better way to get local users logged into the domain
+ in this list.
+ ********************************************************************/
+
+static struct dom_usr *get_domain_userlist(TALLOC_CTX *mem_ctx)
+{
+ struct sessionid *session_list = NULL;
+ char *machine_name, *p, *nm;
+ const char *sep;
+ struct dom_usr *users, *tmp;
+ int i, num_users, num_sessions;
+
+ sep = lp_winbind_separator();
+ if (!sep) {
+ sep = "\\";
+ }
+
+ num_sessions = list_sessions(mem_ctx, &session_list);
+ if (num_sessions == 0) {
+ errno = 0;
+ return NULL;
+ }
+
+ users = talloc_array(mem_ctx, struct dom_usr, num_sessions);
+ if (users == NULL) {
+ TALLOC_FREE(session_list);
+ return NULL;
+ }
+
+ for (i=num_users=0; i<num_sessions; i++) {
+ if (!session_list[i].username
+ || !session_list[i].remote_machine) {
+ continue;
+ }
+ p = strpbrk(session_list[i].remote_machine, "./");
+ if (p) {
+ *p = '\0';
+ }
+ machine_name = talloc_asprintf_strupper_m(
+ users, "%s", session_list[i].remote_machine);
+ if (machine_name == NULL) {
+ DEBUG(10, ("talloc_asprintf failed\n"));
+ continue;
+ }
+ if (strcmp(machine_name, global_myname()) == 0) {
+ p = session_list[i].username;
+ nm = strstr(p, sep);
+ if (nm) {
+ /*
+ * "domain+name" format so split domain and
+ * name components
+ */
+ *nm = '\0';
+ nm += strlen(sep);
+ users[num_users].domain =
+ talloc_asprintf_strupper_m(users,
+ "%s", p);
+ users[num_users].name = talloc_strdup(users,
+ nm);
+ } else {
+ /*
+ * Simple user name so get domain from smb.conf
+ */
+ users[num_users].domain =
+ talloc_strdup(users, lp_workgroup());
+ users[num_users].name = talloc_strdup(users,
+ p);
+ }
+ users[num_users].login_time =
+ session_list[i].connect_start;
+ num_users++;
+ }
+ TALLOC_FREE(machine_name);
+ }
+ TALLOC_FREE(session_list);
+
+ tmp = talloc_realloc(mem_ctx, users, struct dom_usr, num_users);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ users = tmp;
+
+ /* Sort the user list by time, oldest first */
+ if (num_users > 1) {
+ qsort(users, num_users, sizeof(struct dom_usr), dom_user_cmp);
+ }
+
+ errno = 0;
+ return users;
+}
+
+/*******************************************************************
+ RPC Workstation Service request NetWkstaGetInfo with level 100.
+ Returns to the requester:
+ - The machine name.
+ - The smb version number
+ - The domain name.
+ Returns a filled in wkssvc_NetWkstaInfo100 struct.
********************************************************************/
static struct wkssvc_NetWkstaInfo100 *create_wks_info_100(TALLOC_CTX *mem_ctx)
@@ -94,6 +278,14 @@ static struct wkssvc_NetWkstaInfo100 *create_wks_info_100(TALLOC_CTX *mem_ctx)
return info100;
}
+/*******************************************************************
+ RPC Workstation Service request NetWkstaGetInfo with level 101.
+ Returns to the requester:
+ - As per NetWkstaGetInfo with level 100, plus:
+ - The LANMAN directory path (not currently supported).
+ Returns a filled in wkssvc_NetWkstaInfo101 struct.
+ ********************************************************************/
+
static struct wkssvc_NetWkstaInfo101 *create_wks_info_101(TALLOC_CTX *mem_ctx)
{
struct wkssvc_NetWkstaInfo101 *info101;
@@ -111,11 +303,19 @@ static struct wkssvc_NetWkstaInfo101 *create_wks_info_101(TALLOC_CTX *mem_ctx)
info101, "%s", global_myname());
info101->domain_name = talloc_asprintf_strupper_m(
info101, "%s", lp_workgroup());
- info101->lan_root = NULL;
+ info101->lan_root = "";
return info101;
}
+/*******************************************************************
+ RPC Workstation Service request NetWkstaGetInfo with level 102.
+ Returns to the requester:
+ - As per NetWkstaGetInfo with level 101, plus:
+ - The number of logged in users.
+ Returns a filled in wkssvc_NetWkstaInfo102 struct.
+ ********************************************************************/
+
static struct wkssvc_NetWkstaInfo102 *create_wks_info_102(TALLOC_CTX *mem_ctx)
{
struct wkssvc_NetWkstaInfo102 *info102;
@@ -134,35 +334,63 @@ static struct wkssvc_NetWkstaInfo102 *create_wks_info_102(TALLOC_CTX *mem_ctx)
info102, "%s", global_myname());
info102->domain_name = talloc_asprintf_strupper_m(
info102, "%s", lp_workgroup());
- info102->lan_root = NULL;
+ info102->lan_root = "";
users = get_logged_on_userlist(talloc_tos());
info102->logged_on_users = talloc_array_length(users);
+
TALLOC_FREE(users);
return info102;
}
/********************************************************************
- only supports info level 100 at the moment.
+ Handling for RPC Workstation Service request NetWkstaGetInfo
********************************************************************/
WERROR _wkssvc_NetWkstaGetInfo(pipes_struct *p, struct wkssvc_NetWkstaGetInfo *r)
{
switch (r->in.level) {
case 100:
+ /* Level 100 can be allowed from anyone including anonymous
+ * so no access checks are needed for this case */
r->out.info->info100 = create_wks_info_100(p->mem_ctx);
if (r->out.info->info100 == NULL) {
return WERR_NOMEM;
}
break;
case 101:
+ /* Level 101 can be allowed from any logged in user */
+ if (!nt_token_check_sid(&global_sid_Authenticated_Users,
+ p->server_info->ptok)) {
+ DEBUG(1,("User not allowed for NetWkstaGetInfo level "
+ "101\n"));
+ DEBUGADD(3,(" - does not have sid for Authenticated "
+ "Users %s:\n",
+ sid_string_dbg(
+ &global_sid_Authenticated_Users)));
+ debug_nt_user_token(DBGC_CLASS, 3,
+ p->server_info->ptok);
+ return WERR_ACCESS_DENIED;
+ }
r->out.info->info101 = create_wks_info_101(p->mem_ctx);
if (r->out.info->info101 == NULL) {
return WERR_NOMEM;
}
break;
case 102:
+ /* Level 102 Should only be allowed from a domain administrator */
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ p->server_info->ptok)) {
+ DEBUG(1,("User not allowed for NetWkstaGetInfo level "
+ "102\n"));
+ DEBUGADD(3,(" - does not have sid for Administrators "
+ "group %s, sids are:\n",
+ sid_string_dbg(&global_sid_Builtin_Administrators)));
+ debug_nt_user_token(DBGC_CLASS, 3,
+ p->server_info->ptok);
+ return WERR_ACCESS_DENIED;
+ }
r->out.info->info102 = create_wks_info_102(p->mem_ctx);
if (r->out.info->info102 == NULL) {
return WERR_NOMEM;
@@ -185,6 +413,13 @@ WERROR _wkssvc_NetWkstaSetInfo(pipes_struct *p, struct wkssvc_NetWkstaSetInfo *r
return WERR_NOT_SUPPORTED;
}
+/********************************************************************
+ RPC Workstation Service request NetWkstaEnumUsers with level 0:
+ Returns to the requester:
+ - the user names of the logged in users.
+ Returns a filled in wkssvc_NetWkstaEnumUsersCtr0 struct.
+ ********************************************************************/
+
static struct wkssvc_NetWkstaEnumUsersCtr0 *create_enum_users0(
TALLOC_CTX *mem_ctx)
{
@@ -198,12 +433,14 @@ static struct wkssvc_NetWkstaEnumUsersCtr0 *create_enum_users0(
}
users = get_logged_on_userlist(talloc_tos());
- if (users == NULL) {
+ if (users == NULL && errno != 0) {
+ DEBUG(1,("get_logged_on_userlist error %d: %s\n",
+ errno, strerror(errno)));
TALLOC_FREE(ctr0);
return NULL;
}
- num_users = talloc_array_length(users);
+ num_users = (users) ? talloc_array_length(users) : 0;
ctr0->entries_read = num_users;
ctr0->user0 = talloc_array(ctr0, struct wkssvc_NetrWkstaUserInfo0,
num_users);
@@ -221,21 +458,142 @@ static struct wkssvc_NetWkstaEnumUsersCtr0 *create_enum_users0(
}
/********************************************************************
+ RPC Workstation Service request NetWkstaEnumUsers with level 1.
+ Returns to the requester:
+ - the user names of the logged in users,
+ - the domain or machine each is logged into,
+ - the password server that was used to authenticate each,
+ - other domains each user is logged into (not currently supported).
+ Returns a filled in wkssvc_NetWkstaEnumUsersCtr1 struct.
+ ********************************************************************/
+
+static struct wkssvc_NetWkstaEnumUsersCtr1 *create_enum_users1(
+ TALLOC_CTX *mem_ctx)
+{
+ struct wkssvc_NetWkstaEnumUsersCtr1 *ctr1;
+ char **users;
+ struct dom_usr *dom_users;
+ char *pwd_server;
+ int i, j, num_users, num_dom_users;
+
+ ctr1 = talloc(mem_ctx, struct wkssvc_NetWkstaEnumUsersCtr1);
+ if (ctr1 == NULL) {
+ return NULL;
+ }
+
+ users = get_logged_on_userlist(talloc_tos());
+ if (users == NULL && errno != 0) {
+ DEBUG(1,("get_logged_on_userlist error %d: %s\n",
+ errno, strerror(errno)));
+ TALLOC_FREE(ctr1);
+ return NULL;
+ }
+ num_users = (users) ? talloc_array_length(users) : 0;
+
+ dom_users = get_domain_userlist(talloc_tos());
+ if (dom_users == NULL && errno != 0) {
+ TALLOC_FREE(ctr1);
+ TALLOC_FREE(users);
+ return NULL;
+ }
+ num_dom_users = (dom_users) ? talloc_array_length(dom_users) : 0;
+
+ ctr1->user1 = talloc_array(ctr1, struct wkssvc_NetrWkstaUserInfo1,
+ num_users+num_dom_users);
+ if (ctr1->user1 == NULL) {
+ TALLOC_FREE(ctr1);
+ TALLOC_FREE(users);
+ TALLOC_FREE(dom_users);
+ return NULL;
+ }
+
+ if ((pwd_server = talloc_strdup(ctr1->user1, lp_passwordserver()))) {
+ /* The configured password server is a full DNS name but
+ * for the logon server we need to return just the first
+ * component (machine name) of it in upper-case */
+ char *p = strchr(pwd_server, '.');
+ if (p) {
+ *p = '\0';
+ } else {
+ p = pwd_server + strlen(pwd_server);
+ }
+ while (--p >= pwd_server) {
+ *p = toupper(*p);
+ }
+ } else {
+ pwd_server = "";
+ }
+
+ /* Put in local users first */
+ for (i=0; i<num_users; i++) {
+ ctr1->user1[i].user_name = talloc_move(ctr1->user1, &users[i]);
+
+ /* For a local user the domain name and logon server are
+ * both returned as the local machine's NetBIOS name */
+ ctr1->user1[i].logon_domain = ctr1->user1[i].logon_server =
+ talloc_asprintf_strupper_m(ctr1->user1, "%s", global_myname());
+
+ ctr1->user1[i].other_domains = NULL; /* Maybe in future? */
+ }
+
+ /* Now domain users */
+ for (j=0; j<num_dom_users; j++) {
+ ctr1->user1[i].user_name =
+ talloc_strdup(ctr1->user1, dom_users[j].name);
+ ctr1->user1[i].logon_domain =
+ talloc_strdup(ctr1->user1, dom_users[j].domain);
+ ctr1->user1[i].logon_server = pwd_server;
+
+ ctr1->user1[i++].other_domains = NULL; /* Maybe in future? */
+ }
+
+ ctr1->entries_read = i;
+
+ TALLOC_FREE(users);
+ TALLOC_FREE(dom_users);
+ return ctr1;
+}
+
+/********************************************************************
+ Handling for RPC Workstation Service request NetWkstaEnumUsers
+ (a.k.a Windows NetWkstaUserEnum)
********************************************************************/
WERROR _wkssvc_NetWkstaEnumUsers(pipes_struct *p, struct wkssvc_NetWkstaEnumUsers *r)
{
- if (r->in.info->level != 0) {
- return WERR_UNKNOWN_LEVEL;
+ /* This with any level should only be allowed from a domain administrator */
+ if (!nt_token_check_sid(&global_sid_Builtin_Administrators,
+ p->server_info->ptok)) {
+ DEBUG(1,("User not allowed for NetWkstaEnumUsers\n"));
+ DEBUGADD(3,(" - does not have sid for Administrators group "
+ "%s\n", sid_string_dbg(
+ &global_sid_Builtin_Administrators)));
+ debug_nt_user_token(DBGC_CLASS, 3, p->server_info->ptok);
+ return WERR_ACCESS_DENIED;
}
- r->out.info->ctr.user0 = create_enum_users0(p->mem_ctx);
- if (r->out.info->ctr.user0 == NULL) {
- return WERR_NOMEM;
+ switch (r->in.info->level) {
+ case 0:
+ r->out.info->ctr.user0 = create_enum_users0(p->mem_ctx);
+ if (r->out.info->ctr.user0 == NULL) {
+ return WERR_NOMEM;
+ }
+ r->out.info->level = r->in.info->level;
+ *r->out.entries_read = r->out.info->ctr.user0->entries_read;
+ *r->out.resume_handle = 0;
+ break;
+ case 1:
+ r->out.info->ctr.user1 = create_enum_users1(p->mem_ctx);
+ if (r->out.info->ctr.user1 == NULL) {
+ return WERR_NOMEM;
+ }
+ r->out.info->level = r->in.info->level;
+ *r->out.entries_read = r->out.info->ctr.user1->entries_read;
+ *r->out.resume_handle = 0;
+ break;
+ default:
+ return WERR_UNKNOWN_LEVEL;
}
- r->out.info->level = r->in.info->level;
- *r->out.entries_read = r->out.info->ctr.user0->entries_read;
- *r->out.resume_handle = 0;
return WERR_OK;
}