Index: source/utils/net_help.c =================================================================== --- source/utils/net_help.c (revision 681) +++ source/utils/net_help.c (working copy) @@ -95,7 +95,9 @@ "\n\tDelete specified group\n"); d_printf("\nnet [] group ADD [-C comment] [-c container]"\ " [misc. options] [targets]\n\tCreate specified group\n"); - d_printf("\nnet rpc group MEMBERS \n\tList Group Members\n\n"); + d_printf("\nnet [] group MEMBERS \n\tList Group Members\n\n"); + d_printf("\nnet [] group ADDMEM \n\tAdd a Member to a Group\n\n"); + d_printf("\nnet [] group DELMEM \n\tDelete a Member from a Group\n\n"); net_common_methods_usage(argc, argv); net_common_flags_usage(argc, argv); d_printf("\t-C or --comment=\tdescriptive comment (for add only)\n"); Index: source/utils/net_ads.c =================================================================== --- source/utils/net_ads.c (revision 681) +++ source/utils/net_ads.c (working copy) @@ -37,7 +37,8 @@ "\nnet ads user"\ "\n\tlist, add, or delete users in the realm\n"\ "\nnet ads group"\ -"\n\tlist, add, or delete groups in the realm\n"\ +"\n\tlist, add, or delete groups in the realm"\ +"\n\tlist, add, or delete members from a group in the realm\n"\ "\nnet ads info"\ "\n\tshows some info on the server\n"\ "\nnet ads status"\ @@ -447,6 +448,407 @@ return net_help_group(argc, argv); } + + + +static int ads_group_member_internal(char mode,int argc, const char **argv) +{ + ADS_STATUS status; + int i,j; + char *dn,*sn; + char *ldap_exp; + ADS_MODLIST mods; + BOOL more_values; + char *escaped_group; + char *descr; + const char **attrs; + uint32 first_usn; + uint32 current_usn; + char *group_dn=NULL; + ADS_STRUCT *ads = NULL; + void *res = NULL; + void *res1 = NULL; + TALLOC_CTX *ctx = NULL; + char **grouplist = NULL; + size_t numingrouplist = 0; + char **modgrouplist = NULL; + size_t numinmodgrouplist = 0; + char **dntargetlist = NULL; + size_t numindntargetlist = 0; + int rc = -1; + int num_retries = 0; + + + /* needs to have at least group name) */ + if (argc < 1) return net_ads_group_usage(argc, argv); + + /* need to have group name and member name if adding/deleting */ + if (((mode == 'A')||(mode == 'D'))&&(argc < 2)) { + return net_ads_group_usage(argc, argv); + } + + /* start up ads */ + if (!(ads = ads_startup())) { + d_printf("ads_group_member_internal: ads_startup() failed!\n"); + goto done; + } + + + /* init talloc context */ + if (!(ctx = talloc_init("ads_group_member_internal"))) { + d_printf("ads_group_member_internal: talloc_init() failed!\n"); + goto done; + } + + + /* escape the group name */ + escaped_group = escape_ldap_string_alloc(argv[0]); + if (!escaped_group) { + d_printf("ads_group_member_internal: escape_ldap_string_alloc() failed!\n"); + goto done; + } + + + + /* Need to look up the group and all existing members in it */ + + /* vvvvv Begin code adapted from nsswitch/winbindd_ads.c:lookup_groupmem() vvvvv */ + /* which nicely illustrates how to fetch members from a very */ + /* large group, which could change while being read */ + + if (!(ldap_exp = talloc_asprintf(ctx, "(&(sAMAccountName=%s)(objectclass=group))",escaped_group))) { + d_printf("ads_group_member_internal: talloc_asprintf for ldap_exp failed!\n"); + goto done; + } + ads_memfree(ads, escaped_group); + + grouplist = NULL; + numingrouplist = 0; + + // code below this section depends on attrs allocation as well + attrs = talloc(ctx, 3 * sizeof(*attrs)); + attrs[1] = talloc_strdup(ctx, "usnChanged"); + attrs[2] = NULL; + + do { + if (numingrouplist == 0) + attrs[0] = talloc_strdup(ctx, "member"); + + status = ads_search_retry(ads, &res, ldap_exp, attrs); + + if (!ADS_ERR_OK(status) || !res) { + d_printf("ads_group_member_internal: %s\n",ads_errstr(status)); + goto done; + } + + if(!ads_count_replies(ads, res)) { + if (numingrouplist == 0) { + d_printf("Group, \"%s\", doesn't exist!\n",argv[0]); + goto done; + } + break; + } + + if (numingrouplist == 0) { + group_dn = ads_get_dn(ads, res); + + if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) { + d_printf("ads_group_member_internal: could not pull usnChanged!\n"); + goto done; + } + } + + if (!ads_pull_uint32(ads, res, "usnChanged", ¤t_usn)) { + d_printf("ads_group_member_internal: could not pull usnChanged!\n"); + goto done; + } + + if (first_usn != current_usn) { + DEBUG(5, ("ads_group_member_internal: USN on this record changed" + " - restarting search\n")); + if (num_retries < 5) { + num_retries++; + numingrouplist = 0; + continue; + } else { + d_printf("ads_group_member_internal: USN on this record changed - restarted search too many times, aborting!\n"); + goto done; + } + } + + grouplist = ads_pull_strings_range(ads, ctx, res, + "member", + grouplist, + &attrs[0], + &numingrouplist, + &more_values); + + if ((grouplist == NULL) || (numingrouplist == 0)) + break; + + } while (more_values); + + + /* ^^^^^ End code adapted from nsswitch/winbindd_ads.c:lookup_groupmem() ^^^^^ */ + + + + /* list members */ + if (mode == 'L') { + + if (opt_long_list_entries) + d_printf("\nMember name Comment"\ + "\n-----------------------------\n"); + + attrs[0] = talloc_strdup(ctx,"sAMAccountName"); + attrs[1] = talloc_strdup(ctx,"description"); + attrs[2] = NULL; + /* nb: attrs limited to 3 in earlier allocation */ + + for (i=0;i