From 6ca83b9d015479ccddc45598974da2045bea3033 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 3 Aug 2009 16:52:01 +0200 Subject: [PATCH] Support NetWkstaGetInfo 101 and 102 --- source/configure.in | 2 +- source/rpc_server/srv_wkssvc_nt.c | 128 +++++++++++++++++++++++++++++++---- 2 files changed, 114 insertions(+), 16 deletions(-) diff --git a/source/configure.in b/source/configure.in index 7cfd3fb..068f9cd 100644 --- a/source/configure.in +++ b/source/configure.in @@ -2016,7 +2016,7 @@ dnl We need to check for many of them dnl But we don't need to do each and every one, because our code uses dnl mostly just the utmp (not utmpx) fields. -AC_CHECK_FUNCS(pututline pututxline updwtmp updwtmpx getutmpx) +AC_CHECK_FUNCS(pututline pututxline updwtmp updwtmpx getutmpx getutxent) AC_CACHE_CHECK([for ut_name in utmp],samba_cv_HAVE_UT_UT_NAME,[ AC_TRY_COMPILE([#include --- diff -u a/source/include/config.h.in b/source/include/config.h.in --- a/source/include/config.h.in 2008-09-18 04:58:36.000000000 -0700 +++ b/source/include/config.h.in 2009-08-06 13:42:20.000000000 -0700 @@ -676,6 +676,9 @@ /* Define to 1 if you have the `getutmpx' function. */ #undef HAVE_GETUTMPX +/* Define to 1 if you have the `getutxent' function. */ +#undef HAVE_GETUTXENT + /* Define to 1 if you have the `getxattr' function. */ #undef HAVE_GETXATTR From: Ian Puleston Date: Tue, 24 Nov 2009 Subject: [PATCH 3/3] Complete support for NetWkstaGetInfo/NetWkstaEnumUsers --- diff --git a/source/rpc_server/srv_wkssvc_nt.c b/source/rpc_server/srv_wkssvc_nt.c index 1a827b2..a87b1dd 100644 --- a/source/rpc_server/srv_wkssvc_nt.c +++ b/source/rpc_server/srv_wkssvc_nt.c @@ -27,52 +27,395 @@ #undef DBGC_CLASS #define DBGC_CLASS DBGC_RPC_SRV +/* Back mappings from version 3.2+: */ +#define wkssvc_NetrWkstaUserInfo0 USER_INFO_0 +#define wkssvc_NetWkstaEnumUsersCtr0 USER_INFO_0_CONTAINER +#define wkssvc_NetrWkstaUserInfo1 USER_INFO_1 +#define wkssvc_NetWkstaEnumUsersCtr1 USER_INFO_1_CONTAINER + +struct dom_usr { + char *name; + char *domain; + time_t login_time; +}; + +#ifdef HAVE_GETUTXENT + +#include + +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; + int i, num_users = 0; + struct usrinfo *usr_infos = NULL; + struct utmpx *u; + + while ((u = getutxent()) != NULL) { + struct usrinfo *tmp; + if (u->ut_type != USER_PROCESS) { + continue; + } + 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; + } + 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; +} + +#else + +static char **get_logged_on_userlist(TALLOC_CTX *mem_ctx) +{ + return NULL; +} + +#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); +} + +/******************************************************************* + 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(&session_list); + if (num_sessions == 0) { + errno = 0; + return NULL; + } + + users = talloc_array(mem_ctx, struct dom_usr, num_sessions); + if (users == NULL) { + SAFE_FREE(session_list); + return NULL; + } + + for (i=num_users=0; i 1) { + qsort(users, num_users, sizeof(struct dom_usr), dom_user_cmp); + } + + errno = 0; + return users; +} + /******************************************************************* - Fill in the valiues for the struct wkssvc_NetWkstaInfo100. + 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 void create_wks_info_100(struct wkssvc_NetWkstaInfo100 *info100) +static struct wkssvc_NetWkstaInfo100 *create_wks_info_100(TALLOC_CTX *mem_ctx) { - pstring my_name; - pstring domain; + struct wkssvc_NetWkstaInfo100 *info100; + char *ptr; - pstrcpy (my_name, global_myname()); - strupper_m(my_name); + info100 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo100); + if (info100 == NULL) { + return NULL; + } - pstrcpy (domain, lp_workgroup()); - strupper_m(domain); - - info100->platform_id = 0x000001f4; /* unknown */ - info100->version_major = lp_major_announce_version(); - info100->version_minor = lp_minor_announce_version(); + info100->platform_id = PLATFORM_ID_NT; /* unknown */ + info100->version_major = lp_major_announce_version(); + info100->version_minor = lp_minor_announce_version(); - info100->server_name = talloc_strdup( info100, my_name ); - info100->domain_name = talloc_strdup( info100, domain ); + ptr = talloc_asprintf(info100, "%s", global_myname()); + if (ptr) strupper_m(ptr); + info100->server_name = ptr; + ptr = talloc_asprintf(info100, "%s", lp_workgroup()); + if (ptr) strupper_m(ptr); + info100->domain_name = ptr; - return; + return info100; } -/******************************************************************** - only supports info level 100 at the moment. +/******************************************************************* + 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. ********************************************************************/ -WERROR _wkssvc_NetWkstaGetInfo( pipes_struct *p, struct wkssvc_NetWkstaGetInfo *r) +static struct wkssvc_NetWkstaInfo101 *create_wks_info_101(TALLOC_CTX *mem_ctx) { - struct wkssvc_NetWkstaInfo100 *wks100 = NULL; - - /* We only support info level 100 currently */ - - if ( r->in.level != 100 ) { - return WERR_UNKNOWN_LEVEL; + struct wkssvc_NetWkstaInfo101 *info101; + char *ptr; + + info101 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo101); + if (info101 == NULL) { + return NULL; } - if ( (wks100 = TALLOC_ZERO_P(p->mem_ctx, struct wkssvc_NetWkstaInfo100)) == NULL ) { - return WERR_NOMEM; + info101->platform_id = PLATFORM_ID_NT; /* unknown */ + info101->version_major = lp_major_announce_version(); + info101->version_minor = lp_minor_announce_version(); + + ptr = talloc_asprintf(info101, "%s", global_myname()); + if (ptr) strupper_m(ptr); + info101->server_name = ptr; + ptr = talloc_asprintf(info101, "%s", lp_workgroup()); + if (ptr) strupper_m(ptr); + info101->domain_name = ptr; + 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; + char **users; + char *ptr; + + info102 = talloc(mem_ctx, struct wkssvc_NetWkstaInfo102); + if (info102 == NULL) { + return NULL; } - create_wks_info_100( wks100 ); - - r->out.info->info100 = wks100; + info102->platform_id = PLATFORM_ID_NT; /* unknown */ + info102->version_major = lp_major_announce_version(); + info102->version_minor = lp_minor_announce_version(); + + ptr = talloc_asprintf(info102, "%s", global_myname()); + if (ptr) strupper_m(ptr); + info102->server_name = ptr; + ptr = talloc_asprintf(info102, "%s", lp_workgroup()); + if (ptr) strupper_m(ptr); + info102->domain_name = ptr; + info102->lan_root = ""; + + users = get_logged_on_userlist(NULL); + info102->logged_on_users = talloc_array_length(users); + + TALLOC_FREE(users); + + return info102; +} + +/******************************************************************** + 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->pipe_user.nt_user_token)) { + DEBUG(1,("User not allowed for NetWkstaGetInfo level " + "101\n")); + DEBUGADD(3,(" - does not have sid for Authenticated " + "Users %s:\n", + sid_string_static( + &global_sid_Authenticated_Users))); + debug_nt_user_token(DBGC_CLASS, 3, + p->pipe_user.nt_user_token); + 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->pipe_user.nt_user_token)) { + 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_static(&global_sid_Builtin_Administrators))); + debug_nt_user_token(DBGC_CLASS, 3, + p->pipe_user.nt_user_token); + return WERR_ACCESS_DENIED; + } + r->out.info->info102 = create_wks_info_102(p->mem_ctx); + if (r->out.info->info102 == NULL) { + return WERR_NOMEM; + } + break; + default: + return WERR_UNKNOWN_LEVEL; + } return WERR_OK; } @@ -88,13 +431,189 @@ WERROR _wkssvc_NetWkstaSetInfo( pipes_struct *p, struct wkssvc_NetWkstaSetInfo * } /******************************************************************** + 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) +{ + struct wkssvc_NetWkstaEnumUsersCtr0 *ctr0; + char **users; + int i, num_users; + + ctr0 = talloc(mem_ctx, struct wkssvc_NetWkstaEnumUsersCtr0); + if (ctr0 == NULL) { + return NULL; + } + + users = get_logged_on_userlist(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 = (users) ? talloc_array_length(users) : 0; + ctr0->entries_read = num_users; + ctr0->user0 = talloc_array(ctr0, struct wkssvc_NetrWkstaUserInfo0, + num_users); + if (ctr0->user0 == NULL) { + TALLOC_FREE(ctr0); + TALLOC_FREE(users); + return NULL; + } + + for (i=0; iuser0[i].user = talloc_move(ctr0->user0, &users[i]); + } + TALLOC_FREE(users); + return ctr0; +} + +/******************************************************************** + 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(NULL); + 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(NULL); + 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; iuser1[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 */ + ptr = talloc_asprintf(ctr1->user1, "%s", global_myname()); + if (ptr) strupper_m(ptr); + ctr1->user1[i].logon_domain = ctr1->user1[i].logon_server = ptr; + + ctr1->user1[i].other_domains = NULL; /* Maybe in future? */ + } + + /* Now domain users */ + for (j=0; juser1[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) { - /* FIXME: Add implementation code here */ - p->rng_fault_state = True; - return WERR_NOT_SUPPORTED; + /* This with any level should only be allowed from a domain administrator */ + if (!nt_token_check_sid(&global_sid_Builtin_Administrators, + p->pipe_user.nt_user_token)) { + DEBUG(1,("User not allowed for NetWkstaEnumUsers\n")); + DEBUGADD(3,(" - does not have sid for Administrators group " + "%s\n", sid_string_static( + &global_sid_Builtin_Administrators))); + debug_nt_user_token(DBGC_CLASS, 3, p->pipe_user.nt_user_token); + return WERR_ACCESS_DENIED; + } + + switch (r->in.level) { + case 0: + r->out.users->user0 = create_enum_users0(p->mem_ctx); + if (r->out.users->user0 == NULL) { + return WERR_NOMEM; + } + *r->out.entriesread = r->out.users->user0->entries_read; + *r->out.resumehandle = 0; + break; + case 1: + r->out.users->user1 = create_enum_users1(p->mem_ctx); + if (r->out.users->user1 == NULL) { + return WERR_NOMEM; + } + *r->out.entriesread = r->out.users->user1->entries_read; + *r->out.resumehandle = 0; + break; + default: + return WERR_UNKNOWN_LEVEL; + } + + return WERR_OK; } /******************************************************************** --- diff --git a/source/librpc/gen_ndr/ndr_wkssvc.c b/source/librpc/gen_ndr/ndr_wkssvc.c index 2af3587..03c3b9d 100644 --- a/source/librpc/gen_ndr/ndr_wkssvc.c +++ b/source/librpc/gen_ndr/ndr_wkssvc.c @@ -1677,6 +1677,10 @@ NTSTATUS ndr_push_WKS_USER_ENUM_UNION(struct ndr_push *ndr, int ndr_flags, const int level; level = ndr_push_get_switch_value(ndr, r); if (ndr_flags & NDR_SCALARS) { + /* First comes the level */ + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, level)); + /* The the wkssvc_NetWkstaEnumUsers container which starts with + * a repeat of the level */ NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, level)); switch (level) { case 0: @@ -2407,8 +2411,8 @@ NTSTATUS ndr_push_wkssvc_NetWkstaEnumUsers(struct ndr_push *ndr, int flags, cons NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_charset_length(r->in.server_name, CH_UTF16))); NDR_CHECK(ndr_push_charset(ndr, NDR_SCALARS, r->in.server_name, ndr_charset_length(r->in.server_name, CH_UTF16), sizeof(uint16_t), CH_UTF16)); } - NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->in.level)); if (r->in.users == NULL) return NT_STATUS_INVALID_PARAMETER_MIX; + NDR_CHECK(ndr_push_set_switch_value(ndr, r->in.users, r->in.level)); NDR_CHECK(ndr_push_WKS_USER_ENUM_UNION(ndr, NDR_SCALARS|NDR_BUFFERS, r->in.users)); NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->in.prefmaxlen)); if (r->in.resumehandle == NULL) return NT_STATUS_INVALID_PARAMETER_MIX; @@ -2416,6 +2420,7 @@ NTSTATUS ndr_push_wkssvc_NetWkstaEnumUsers(struct ndr_push *ndr, int flags, cons } if (flags & NDR_OUT) { if (r->out.users == NULL) return NT_STATUS_INVALID_PARAMETER_MIX; + NDR_CHECK(ndr_push_set_switch_value(ndr, r->out.users, r->in.level)); NDR_CHECK(ndr_push_WKS_USER_ENUM_UNION(ndr, NDR_SCALARS|NDR_BUFFERS, r->out.users)); NDR_CHECK(ndr_push_unique_ptr(ndr, r->out.entriesread)); if (r->out.entriesread) { @@ -2469,6 +2474,7 @@ NTSTATUS ndr_pull_wkssvc_NetWkstaEnumUsers(struct ndr_pull *ndr, int flags, stru } _mem_save_users_0 = NDR_PULL_GET_MEM_CTX(ndr); NDR_PULL_SET_MEM_CTX(ndr, r->in.users, LIBNDR_FLAG_REF_ALLOC); + NDR_CHECK(ndr_pull_set_switch_value(ndr, r->in.users, r->in.level)); NDR_CHECK(ndr_pull_WKS_USER_ENUM_UNION(ndr, NDR_SCALARS|NDR_BUFFERS, r->in.users)); NDR_PULL_SET_MEM_CTX(ndr, _mem_save_users_0, LIBNDR_FLAG_REF_ALLOC); NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->in.prefmaxlen)); @@ -2490,6 +2496,7 @@ NTSTATUS ndr_pull_wkssvc_NetWkstaEnumUsers(struct ndr_pull *ndr, int flags, stru } _mem_save_users_0 = NDR_PULL_GET_MEM_CTX(ndr); NDR_PULL_SET_MEM_CTX(ndr, r->out.users, LIBNDR_FLAG_REF_ALLOC); + NDR_CHECK(ndr_pull_set_switch_value(ndr, r->out.users, r->in.level)); NDR_CHECK(ndr_pull_WKS_USER_ENUM_UNION(ndr, NDR_SCALARS|NDR_BUFFERS, r->out.users)); NDR_PULL_SET_MEM_CTX(ndr, _mem_save_users_0, LIBNDR_FLAG_REF_ALLOC); NDR_CHECK(ndr_pull_generic_ptr(ndr, &_ptr_entriesread)); --- --- diff --git a/source/lib/talloc/talloc.h b/source/lib/talloc/talloc.h index 5431971..1aedb99 100644 --- a/source/lib/talloc/talloc.h +++ b/source/lib/talloc/talloc.h @@ -94,6 +94,7 @@ typedef void TALLOC_CTX; #define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) #define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) #define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) +#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx)) #define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) #define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)