libads/ldap.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003    ads (active directory) utility library
00004    Copyright (C) Andrew Tridgell 2001
00005    Copyright (C) Remus Koos 2001
00006    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
00007    Copyright (C) Guenther Deschner 2005
00008    Copyright (C) Gerald Carter 2006
00009    
00010    This program is free software; you can redistribute it and/or modify
00011    it under the terms of the GNU General Public License as published by
00012    the Free Software Foundation; either version 2 of the License, or
00013    (at your option) any later version.
00014    
00015    This program is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018    GNU General Public License for more details.
00019    
00020    You should have received a copy of the GNU General Public License
00021    along with this program; if not, write to the Free Software
00022    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023 */
00024 
00025 #include "includes.h"
00026 
00027 #ifdef HAVE_LDAP
00028 
00029 /**
00030  * @file ldap.c
00031  * @brief basic ldap client-side routines for ads server communications
00032  *
00033  * The routines contained here should do the necessary ldap calls for
00034  * ads setups.
00035  * 
00036  * Important note: attribute names passed into ads_ routines must
00037  * already be in UTF-8 format.  We do not convert them because in almost
00038  * all cases, they are just ascii (which is represented with the same
00039  * codepoints in UTF-8).  This may have to change at some point
00040  **/
00041 
00042 
00043 #define LDAP_SERVER_TREE_DELETE_OID     "1.2.840.113556.1.4.805"
00044 
00045 static SIG_ATOMIC_T gotalarm;
00046                                                                                                                    
00047 /***************************************************************
00048  Signal function to tell us we timed out.
00049 ****************************************************************/
00050 
00051 static void gotalarm_sig(void)
00052 {
00053         gotalarm = 1;
00054 }
00055 
00056  LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
00057 {
00058         LDAP *ldp = NULL;
00059 
00060         /* Setup timeout */
00061         gotalarm = 0;
00062         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
00063         alarm(to);
00064         /* End setup timeout. */
00065 
00066         ldp = ldap_open(server, port);
00067 
00068         if (ldp == NULL) {
00069                 DEBUG(2,("Could not open LDAP connection to %s:%d: %s\n",
00070                          server, port, strerror(errno)));
00071         }
00072 
00073         /* Teardown timeout. */
00074         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
00075         alarm(0);
00076 
00077         return ldp;
00078 }
00079 
00080 static int ldap_search_with_timeout(LDAP *ld,
00081                                     LDAP_CONST char *base,
00082                                     int scope,
00083                                     LDAP_CONST char *filter,
00084                                     char **attrs,
00085                                     int attrsonly,
00086                                     LDAPControl **sctrls,
00087                                     LDAPControl **cctrls,
00088                                     int sizelimit,
00089                                     LDAPMessage **res )
00090 {
00091         struct timeval timeout;
00092         int result;
00093 
00094         /* Setup timeout for the ldap_search_ext_s call - local and remote. */
00095         timeout.tv_sec = lp_ldap_timeout();
00096         timeout.tv_usec = 0;
00097 
00098         /* Setup alarm timeout.... Do we need both of these ? JRA. */
00099         gotalarm = 0;
00100         CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
00101         alarm(lp_ldap_timeout());
00102         /* End setup timeout. */
00103 
00104         result = ldap_search_ext_s(ld, base, scope, filter, attrs,
00105                                    attrsonly, sctrls, cctrls, &timeout,
00106                                    sizelimit, res);
00107 
00108         /* Teardown timeout. */
00109         CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
00110         alarm(0);
00111 
00112         if (gotalarm != 0)
00113                 return LDAP_TIMELIMIT_EXCEEDED;
00114 
00115         return result;
00116 }
00117 
00118 /**********************************************
00119  Do client and server sitename match ?
00120 **********************************************/
00121 
00122 BOOL ads_sitename_match(ADS_STRUCT *ads)
00123 {
00124         if (ads->config.server_site_name == NULL &&
00125             ads->config.client_site_name == NULL ) {
00126                 DEBUG(10,("ads_sitename_match: both null\n"));
00127                 return True;
00128         }
00129         if (ads->config.server_site_name &&
00130             ads->config.client_site_name &&
00131             strequal(ads->config.server_site_name,
00132                      ads->config.client_site_name)) {
00133                 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
00134                 return True;
00135         }
00136         DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
00137                 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
00138                 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
00139         return False;
00140 }
00141 
00142 /**********************************************
00143  Is this the closest DC ?
00144 **********************************************/
00145 
00146 BOOL ads_closest_dc(ADS_STRUCT *ads)
00147 {
00148         if (ads->config.flags & ADS_CLOSEST) {
00149                 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag set\n"));
00150                 return True;
00151         }
00152 
00153         /* not sure if this can ever happen */
00154         if (ads_sitename_match(ads)) {
00155                 DEBUG(10,("ads_closest_dc: ADS_CLOSEST flag not set but sites match\n"));
00156                 return True;
00157         }
00158 
00159         DEBUG(10,("ads_closest_dc: %s is not the closest DC\n", 
00160                 ads->config.ldap_server_name));
00161 
00162         return False;
00163 }
00164 
00165 
00166 /*
00167   try a connection to a given ldap server, returning True and setting the servers IP
00168   in the ads struct if successful
00169  */
00170 BOOL ads_try_connect(ADS_STRUCT *ads, const char *server )
00171 {
00172         char *srv;
00173         struct cldap_netlogon_reply cldap_reply;
00174 
00175         if (!server || !*server) {
00176                 return False;
00177         }
00178         
00179         DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n", 
00180                 server, ads->server.realm));
00181 
00182         /* this copes with inet_ntoa brokenness */
00183         
00184         srv = SMB_STRDUP(server);
00185 
00186         ZERO_STRUCT( cldap_reply );
00187 
00188         if ( !ads_cldap_netlogon( srv, ads->server.realm, &cldap_reply ) ) {
00189                 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
00190                 SAFE_FREE( srv );
00191                 return False;
00192         }
00193 
00194         /* Check the CLDAP reply flags */
00195 
00196         if ( !(cldap_reply.flags & ADS_LDAP) ) {
00197                 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
00198                         srv));
00199                 SAFE_FREE( srv );
00200                 return False;
00201         }
00202 
00203         /* Fill in the ads->config values */
00204 
00205         SAFE_FREE(ads->config.realm);
00206         SAFE_FREE(ads->config.bind_path);
00207         SAFE_FREE(ads->config.ldap_server_name);
00208         SAFE_FREE(ads->config.server_site_name);
00209         SAFE_FREE(ads->config.client_site_name);
00210         SAFE_FREE(ads->server.workgroup);
00211 
00212         ads->config.flags              = cldap_reply.flags;
00213         ads->config.ldap_server_name   = SMB_STRDUP(cldap_reply.hostname);
00214         strupper_m(cldap_reply.domain);
00215         ads->config.realm              = SMB_STRDUP(cldap_reply.domain);
00216         ads->config.bind_path          = ads_build_dn(ads->config.realm);
00217         if (*cldap_reply.server_site_name) {
00218                 ads->config.server_site_name =
00219                         SMB_STRDUP(cldap_reply.server_site_name);
00220         }
00221         if (*cldap_reply.client_site_name) {
00222                 ads->config.client_site_name =
00223                         SMB_STRDUP(cldap_reply.client_site_name);
00224         }
00225                 
00226         ads->server.workgroup          = SMB_STRDUP(cldap_reply.netbios_domain);
00227 
00228         ads->ldap_port = LDAP_PORT;
00229         ads->ldap_ip = *interpret_addr2(srv);
00230         SAFE_FREE(srv);
00231         
00232         /* Store our site name. */
00233         sitename_store( cldap_reply.domain, cldap_reply.client_site_name );
00234 
00235         return True;
00236 }
00237 
00238 /**********************************************************************
00239  Try to find an AD dc using our internal name resolution routines
00240  Try the realm first and then then workgroup name if netbios is not 
00241  disabled
00242 **********************************************************************/
00243 
00244 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
00245 {
00246         const char *c_realm;
00247         int count, i=0;
00248         struct ip_service *ip_list;
00249         pstring realm;
00250         BOOL got_realm = False;
00251         BOOL use_own_domain = False;
00252         char *sitename;
00253         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
00254 
00255         /* if the realm and workgroup are both empty, assume they are ours */
00256 
00257         /* realm */
00258         c_realm = ads->server.realm;
00259         
00260         if ( !c_realm || !*c_realm ) {
00261                 /* special case where no realm and no workgroup means our own */
00262                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
00263                         use_own_domain = True;
00264                         c_realm = lp_realm();
00265                 }
00266         }
00267         
00268         if (c_realm && *c_realm) 
00269                 got_realm = True;
00270                    
00271         /* we need to try once with the realm name and fallback to the 
00272            netbios domain name if we fail (if netbios has not been disabled */
00273            
00274         if ( !got_realm && !lp_disable_netbios() ) {
00275                 c_realm = ads->server.workgroup;
00276                 if (!c_realm || !*c_realm) {
00277                         if ( use_own_domain )
00278                                 c_realm = lp_workgroup();
00279                 }
00280                 
00281                 if ( !c_realm || !*c_realm ) {
00282                         DEBUG(0,("ads_find_dc: no realm or workgroup!  Don't know what to do\n"));
00283                         return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
00284                 }
00285         }
00286         
00287         pstrcpy( realm, c_realm );
00288 
00289         sitename = sitename_fetch(realm);
00290 
00291  again:
00292 
00293         DEBUG(6,("ads_find_dc: looking for %s '%s'\n", 
00294                 (got_realm ? "realm" : "domain"), realm));
00295 
00296         status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
00297         if (!NT_STATUS_IS_OK(status)) {
00298                 /* fall back to netbios if we can */
00299                 if ( got_realm && !lp_disable_netbios() ) {
00300                         got_realm = False;
00301                         goto again;
00302                 }
00303                 
00304                 SAFE_FREE(sitename);
00305                 return status;
00306         }
00307 
00308         /* if we fail this loop, then giveup since all the IP addresses returned were dead */
00309         for ( i=0; i<count; i++ ) {
00310                 fstring server;
00311                 
00312                 fstrcpy( server, inet_ntoa(ip_list[i].ip) );
00313                 
00314                 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
00315                         continue;
00316 
00317                 if (!got_realm) {
00318                         /* realm in this case is a workgroup name. We need
00319                            to ignore any IP addresses in the negative connection
00320                            cache that match ip addresses returned in the ad realm
00321                            case. It sucks that I have to reproduce the logic above... */
00322                         c_realm = ads->server.realm;
00323                         if ( !c_realm || !*c_realm ) {
00324                                 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
00325                                         c_realm = lp_realm();
00326                                 }
00327                         }
00328                         if (c_realm && *c_realm &&
00329                                         !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
00330                                 /* Ensure we add the workgroup name for this
00331                                    IP address as negative too. */
00332                                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
00333                                 continue;
00334                         }
00335                 }
00336                         
00337                 if ( ads_try_connect(ads, server) ) {
00338                         SAFE_FREE(ip_list);
00339                         SAFE_FREE(sitename);
00340                         return NT_STATUS_OK;
00341                 }
00342                 
00343                 /* keep track of failures */
00344                 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
00345         }
00346 
00347         SAFE_FREE(ip_list);
00348 
00349         /* In case we failed to contact one of our closest DC on our site we
00350          * need to try to find another DC, retry with a site-less SRV DNS query
00351          * - Guenther */
00352 
00353         if (sitename) {
00354                 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
00355                                 "trying to find another DC\n", sitename));
00356                 SAFE_FREE(sitename);
00357                 namecache_delete(realm, 0x1C);
00358                 goto again;
00359         }
00360 
00361         return NT_STATUS_NO_LOGON_SERVERS;
00362 }
00363 
00364 
00365 /**
00366  * Connect to the LDAP server
00367  * @param ads Pointer to an existing ADS_STRUCT
00368  * @return status of connection
00369  **/
00370 ADS_STATUS ads_connect(ADS_STRUCT *ads)
00371 {
00372         int version = LDAP_VERSION3;
00373         ADS_STATUS status;
00374         NTSTATUS ntstatus;
00375 
00376         ads->last_attempt = time(NULL);
00377         ads->ld = NULL;
00378 
00379         /* try with a user specified server */
00380 
00381         if (ads->server.ldap_server && 
00382             ads_try_connect(ads, ads->server.ldap_server)) {
00383                 goto got_connection;
00384         }
00385 
00386         ntstatus = ads_find_dc(ads);
00387         if (NT_STATUS_IS_OK(ntstatus)) {
00388                 goto got_connection;
00389         }
00390 
00391         return ADS_ERROR_NT(ntstatus);
00392 
00393 got_connection:
00394         DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
00395 
00396         if (!ads->auth.user_name) {
00397                 /* Must use the userPrincipalName value here or sAMAccountName
00398                    and not servicePrincipalName; found by Guenther Deschner */
00399 
00400                 asprintf(&ads->auth.user_name, "%s$", global_myname() );
00401         }
00402 
00403         if (!ads->auth.realm) {
00404                 ads->auth.realm = SMB_STRDUP(ads->config.realm);
00405         }
00406 
00407         if (!ads->auth.kdc_server) {
00408                 ads->auth.kdc_server = SMB_STRDUP(inet_ntoa(ads->ldap_ip));
00409         }
00410 
00411 #if KRB5_DNS_HACK
00412         /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
00413            to MIT kerberos to work (tridge) */
00414         {
00415                 char *env;
00416                 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
00417                 setenv(env, ads->auth.kdc_server, 1);
00418                 free(env);
00419         }
00420 #endif
00421 
00422         /* If the caller() requested no LDAP bind, then we are done */
00423         
00424         if (ads->auth.flags & ADS_AUTH_NO_BIND) {
00425                 return ADS_SUCCESS;
00426         }
00427         
00428         /* Otherwise setup the TCP LDAP session */
00429 
00430         if ( (ads->ld = ldap_open_with_timeout(ads->config.ldap_server_name, 
00431                 LDAP_PORT, lp_ldap_timeout())) == NULL )
00432         {
00433                 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
00434         }
00435 
00436         /* cache the successful connection for workgroup and realm */
00437         if (ads_closest_dc(ads)) {
00438                 saf_store( ads->server.workgroup, inet_ntoa(ads->ldap_ip));
00439                 saf_store( ads->server.realm, inet_ntoa(ads->ldap_ip));
00440         }
00441 
00442         ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
00443 
00444         status = ADS_ERROR(smb_ldap_start_tls(ads->ld, version));
00445         if (!ADS_ERR_OK(status)) {
00446                 return status;
00447         }
00448 
00449         /* fill in the current time and offsets */
00450         
00451         status = ads_current_time( ads );
00452         if ( !ADS_ERR_OK(status) ) {
00453                 return status;
00454         }
00455 
00456         /* Now do the bind */
00457         
00458         if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
00459                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, NULL, NULL));
00460         }
00461 
00462         if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
00463                 return ADS_ERROR(ldap_simple_bind_s( ads->ld, ads->auth.user_name, ads->auth.password));
00464         }
00465 
00466         return ads_sasl_bind(ads);
00467 }
00468 
00469 /*
00470   Duplicate a struct berval into talloc'ed memory
00471  */
00472 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
00473 {
00474         struct berval *value;
00475 
00476         if (!in_val) return NULL;
00477 
00478         value = TALLOC_ZERO_P(ctx, struct berval);
00479         if (value == NULL)
00480                 return NULL;
00481         if (in_val->bv_len == 0) return value;
00482 
00483         value->bv_len = in_val->bv_len;
00484         value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
00485                                               in_val->bv_len);
00486         return value;
00487 }
00488 
00489 /*
00490   Make a values list out of an array of (struct berval *)
00491  */
00492 static struct berval **ads_dup_values(TALLOC_CTX *ctx, 
00493                                       const struct berval **in_vals)
00494 {
00495         struct berval **values;
00496         int i;
00497        
00498         if (!in_vals) return NULL;
00499         for (i=0; in_vals[i]; i++)
00500                 ; /* count values */
00501         values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
00502         if (!values) return NULL;
00503 
00504         for (i=0; in_vals[i]; i++) {
00505                 values[i] = dup_berval(ctx, in_vals[i]);
00506         }
00507         return values;
00508 }
00509 
00510 /*
00511   UTF8-encode a values list out of an array of (char *)
00512  */
00513 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
00514 {
00515         char **values;
00516         int i;
00517        
00518         if (!in_vals) return NULL;
00519         for (i=0; in_vals[i]; i++)
00520                 ; /* count values */
00521         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
00522         if (!values) return NULL;
00523 
00524         for (i=0; in_vals[i]; i++) {
00525                 push_utf8_talloc(ctx, &values[i], in_vals[i]);
00526         }
00527         return values;
00528 }
00529 
00530 /*
00531   Pull a (char *) array out of a UTF8-encoded values list
00532  */
00533 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
00534 {
00535         char **values;
00536         int i;
00537        
00538         if (!in_vals) return NULL;
00539         for (i=0; in_vals[i]; i++)
00540                 ; /* count values */
00541         values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
00542         if (!values) return NULL;
00543 
00544         for (i=0; in_vals[i]; i++) {
00545                 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
00546         }
00547         return values;
00548 }
00549 
00550 /**
00551  * Do a search with paged results.  cookie must be null on the first
00552  *  call, and then returned on each subsequent call.  It will be null
00553  *  again when the entire search is complete 
00554  * @param ads connection to ads server 
00555  * @param bind_path Base dn for the search
00556  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
00557  * @param expr Search expression - specified in local charset
00558  * @param attrs Attributes to retrieve - specified in utf8 or ascii
00559  * @param res ** which will contain results - free res* with ads_msgfree()
00560  * @param count Number of entries retrieved on this page
00561  * @param cookie The paged results cookie to be returned on subsequent calls
00562  * @return status of search
00563  **/
00564 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
00565                                            const char *bind_path,
00566                                            int scope, const char *expr,
00567                                            const char **attrs, void *args,
00568                                            LDAPMessage **res, 
00569                                            int *count, struct berval **cookie)
00570 {
00571         int rc, i, version;
00572         char *utf8_expr, *utf8_path, **search_attrs;
00573         LDAPControl PagedResults, NoReferrals, ExtendedDn, *controls[4], **rcontrols;
00574         BerElement *cookie_be = NULL;
00575         struct berval *cookie_bv= NULL;
00576         BerElement *extdn_be = NULL;
00577         struct berval *extdn_bv= NULL;
00578 
00579         TALLOC_CTX *ctx;
00580         ads_control *external_control = (ads_control *) args;
00581 
00582         *res = NULL;
00583 
00584         if (!(ctx = talloc_init("ads_do_paged_search_args")))
00585                 return ADS_ERROR(LDAP_NO_MEMORY);
00586 
00587         /* 0 means the conversion worked but the result was empty 
00588            so we only fail if it's -1.  In any case, it always 
00589            at least nulls out the dest */
00590         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
00591             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
00592                 rc = LDAP_NO_MEMORY;
00593                 goto done;
00594         }
00595 
00596         if (!attrs || !(*attrs))
00597                 search_attrs = NULL;
00598         else {
00599                 /* This would be the utf8-encoded version...*/
00600                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
00601                 if (!(str_list_copy(&search_attrs, attrs))) {
00602                         rc = LDAP_NO_MEMORY;
00603                         goto done;
00604                 }
00605         }
00606                 
00607                 
00608         /* Paged results only available on ldap v3 or later */
00609         ldap_get_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
00610         if (version < LDAP_VERSION3) {
00611                 rc =  LDAP_NOT_SUPPORTED;
00612                 goto done;
00613         }
00614 
00615         cookie_be = ber_alloc_t(LBER_USE_DER);
00616         if (*cookie) {
00617                 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
00618                 ber_bvfree(*cookie); /* don't need it from last time */
00619                 *cookie = NULL;
00620         } else {
00621                 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
00622         }
00623         ber_flatten(cookie_be, &cookie_bv);
00624         PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
00625         PagedResults.ldctl_iscritical = (char) 1;
00626         PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
00627         PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
00628 
00629         NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
00630         NoReferrals.ldctl_iscritical = (char) 0;
00631         NoReferrals.ldctl_value.bv_len = 0;
00632         NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
00633 
00634         if (external_control && strequal(external_control->control, ADS_EXTENDED_DN_OID)) {
00635 
00636                 ExtendedDn.ldctl_oid = CONST_DISCARD(char *, external_control->control);
00637                 ExtendedDn.ldctl_iscritical = (char) external_control->critical;
00638 
00639                 /* win2k does not accept a ldctl_value beeing passed in */
00640 
00641                 if (external_control->val != 0) {
00642 
00643                         if ((extdn_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
00644                                 rc = LDAP_NO_MEMORY;
00645                                 goto done;
00646                         }
00647 
00648                         if ((ber_printf(extdn_be, "{i}", (ber_int_t) external_control->val)) == -1) {
00649                                 rc = LDAP_NO_MEMORY;
00650                                 goto done;
00651                         }
00652                         if ((ber_flatten(extdn_be, &extdn_bv)) == -1) {
00653                                 rc = LDAP_NO_MEMORY;
00654                                 goto done;
00655                         }
00656 
00657                         ExtendedDn.ldctl_value.bv_len = extdn_bv->bv_len;
00658                         ExtendedDn.ldctl_value.bv_val = extdn_bv->bv_val;
00659 
00660                 } else {
00661                         ExtendedDn.ldctl_value.bv_len = 0;
00662                         ExtendedDn.ldctl_value.bv_val = NULL;
00663                 }
00664 
00665                 controls[0] = &NoReferrals;
00666                 controls[1] = &PagedResults;
00667                 controls[2] = &ExtendedDn;
00668                 controls[3] = NULL;
00669 
00670         } else {
00671                 controls[0] = &NoReferrals;
00672                 controls[1] = &PagedResults;
00673                 controls[2] = NULL;
00674         }
00675 
00676         /* we need to disable referrals as the openldap libs don't
00677            handle them and paged results at the same time.  Using them
00678            together results in the result record containing the server 
00679            page control being removed from the result list (tridge/jmcd) 
00680         
00681            leaving this in despite the control that says don't generate
00682            referrals, in case the server doesn't support it (jmcd)
00683         */
00684         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
00685 
00686         rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr, 
00687                                       search_attrs, 0, controls,
00688                                       NULL, LDAP_NO_LIMIT,
00689                                       (LDAPMessage **)res);
00690 
00691         ber_free(cookie_be, 1);
00692         ber_bvfree(cookie_bv);
00693 
00694         if (rc) {
00695                 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
00696                          ldap_err2string(rc)));
00697                 goto done;
00698         }
00699 
00700         rc = ldap_parse_result(ads->ld, *res, NULL, NULL, NULL,
00701                                         NULL, &rcontrols,  0);
00702 
00703         if (!rcontrols) {
00704                 goto done;
00705         }
00706 
00707         for (i=0; rcontrols[i]; i++) {
00708                 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
00709                         cookie_be = ber_init(&rcontrols[i]->ldctl_value);
00710                         ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
00711                                   &cookie_bv);
00712                         /* the berval is the cookie, but must be freed when
00713                            it is all done */
00714                         if (cookie_bv->bv_len) /* still more to do */
00715                                 *cookie=ber_bvdup(cookie_bv);
00716                         else
00717                                 *cookie=NULL;
00718                         ber_bvfree(cookie_bv);
00719                         ber_free(cookie_be, 1);
00720                         break;
00721                 }
00722         }
00723         ldap_controls_free(rcontrols);
00724 
00725 done:
00726         talloc_destroy(ctx);
00727 
00728         if (extdn_be) {
00729                 ber_free(extdn_be, 1);
00730         }
00731 
00732         if (extdn_bv) {
00733                 ber_bvfree(extdn_bv);
00734         }
00735  
00736         /* if/when we decide to utf8-encode attrs, take out this next line */
00737         str_list_free(&search_attrs);
00738 
00739         return ADS_ERROR(rc);
00740 }
00741 
00742 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
00743                                       int scope, const char *expr,
00744                                       const char **attrs, LDAPMessage **res, 
00745                                       int *count, struct berval **cookie)
00746 {
00747         return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
00748 }
00749 
00750 
00751 /**
00752  * Get all results for a search.  This uses ads_do_paged_search() to return 
00753  * all entries in a large search.
00754  * @param ads connection to ads server 
00755  * @param bind_path Base dn for the search
00756  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
00757  * @param expr Search expression
00758  * @param attrs Attributes to retrieve
00759  * @param res ** which will contain results - free res* with ads_msgfree()
00760  * @return status of search
00761  **/
00762  ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
00763                                    int scope, const char *expr,
00764                                    const char **attrs, void *args,
00765                                    LDAPMessage **res)
00766 {
00767         struct berval *cookie = NULL;
00768         int count = 0;
00769         ADS_STATUS status;
00770 
00771         *res = NULL;
00772         status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
00773                                      &count, &cookie);
00774 
00775         if (!ADS_ERR_OK(status)) 
00776                 return status;
00777 
00778 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
00779         while (cookie) {
00780                 LDAPMessage *res2 = NULL;
00781                 ADS_STATUS status2;
00782                 LDAPMessage *msg, *next;
00783 
00784                 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr, 
00785                                               attrs, args, &res2, &count, &cookie);
00786 
00787                 if (!ADS_ERR_OK(status2)) break;
00788 
00789                 /* this relies on the way that ldap_add_result_entry() works internally. I hope
00790                    that this works on all ldap libs, but I have only tested with openldap */
00791                 for (msg = ads_first_message(ads, res2); msg; msg = next) {
00792                         next = ads_next_message(ads, msg);
00793                         ldap_add_result_entry((LDAPMessage **)res, msg);
00794                 }
00795                 /* note that we do not free res2, as the memory is now
00796                    part of the main returned list */
00797         }
00798 #else
00799         DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
00800         status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
00801 #endif
00802 
00803         return status;
00804 }
00805 
00806  ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
00807                               int scope, const char *expr,
00808                               const char **attrs, LDAPMessage **res)
00809 {
00810         return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
00811 }
00812 
00813 /**
00814  * Run a function on all results for a search.  Uses ads_do_paged_search() and
00815  *  runs the function as each page is returned, using ads_process_results()
00816  * @param ads connection to ads server
00817  * @param bind_path Base dn for the search
00818  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
00819  * @param expr Search expression - specified in local charset
00820  * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
00821  * @param fn Function which takes attr name, values list, and data_area
00822  * @param data_area Pointer which is passed to function on each call
00823  * @return status of search
00824  **/
00825 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
00826                                 int scope, const char *expr, const char **attrs,
00827                                 BOOL(*fn)(char *, void **, void *), 
00828                                 void *data_area)
00829 {
00830         struct berval *cookie = NULL;
00831         int count = 0;
00832         ADS_STATUS status;
00833         LDAPMessage *res;
00834 
00835         status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
00836                                      &count, &cookie);
00837 
00838         if (!ADS_ERR_OK(status)) return status;
00839 
00840         ads_process_results(ads, res, fn, data_area);
00841         ads_msgfree(ads, res);
00842 
00843         while (cookie) {
00844                 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
00845                                              &res, &count, &cookie);
00846 
00847                 if (!ADS_ERR_OK(status)) break;
00848                 
00849                 ads_process_results(ads, res, fn, data_area);
00850                 ads_msgfree(ads, res);
00851         }
00852 
00853         return status;
00854 }
00855 
00856 /**
00857  * Do a search with a timeout.
00858  * @param ads connection to ads server
00859  * @param bind_path Base dn for the search
00860  * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
00861  * @param expr Search expression
00862  * @param attrs Attributes to retrieve
00863  * @param res ** which will contain results - free res* with ads_msgfree()
00864  * @return status of search
00865  **/
00866  ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope, 
00867                           const char *expr,
00868                           const char **attrs, LDAPMessage **res)
00869 {
00870         int rc;
00871         char *utf8_expr, *utf8_path, **search_attrs = NULL;
00872         TALLOC_CTX *ctx;
00873 
00874         *res = NULL;
00875         if (!(ctx = talloc_init("ads_do_search"))) {
00876                 DEBUG(1,("ads_do_search: talloc_init() failed!"));
00877                 return ADS_ERROR(LDAP_NO_MEMORY);
00878         }
00879 
00880         /* 0 means the conversion worked but the result was empty 
00881            so we only fail if it's negative.  In any case, it always 
00882            at least nulls out the dest */
00883         if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
00884             (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
00885                 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
00886                 rc = LDAP_NO_MEMORY;
00887                 goto done;
00888         }
00889 
00890         if (!attrs || !(*attrs))
00891                 search_attrs = NULL;
00892         else {
00893                 /* This would be the utf8-encoded version...*/
00894                 /* if (!(search_attrs = ads_push_strvals(ctx, attrs)))  */
00895                 if (!(str_list_copy(&search_attrs, attrs)))
00896                 {
00897                         DEBUG(1,("ads_do_search: str_list_copy() failed!"));
00898                         rc = LDAP_NO_MEMORY;
00899                         goto done;
00900                 }
00901         }
00902 
00903         /* see the note in ads_do_paged_search - we *must* disable referrals */
00904         ldap_set_option(ads->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
00905 
00906         rc = ldap_search_with_timeout(ads->ld, utf8_path, scope, utf8_expr,
00907                                       search_attrs, 0, NULL, NULL, 
00908                                       LDAP_NO_LIMIT,
00909                                       (LDAPMessage **)res);
00910 
00911         if (rc == LDAP_SIZELIMIT_EXCEEDED) {
00912                 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
00913                 rc = 0;
00914         }
00915 
00916  done:
00917         talloc_destroy(ctx);
00918         /* if/when we decide to utf8-encode attrs, take out this next line */
00919         str_list_free(&search_attrs);
00920         return ADS_ERROR(rc);
00921 }
00922 /**
00923  * Do a general ADS search
00924  * @param ads connection to ads server
00925  * @param res ** which will contain results - free res* with ads_msgfree()
00926  * @param expr Search expression
00927  * @param attrs Attributes to retrieve
00928  * @return status of search
00929  **/
00930  ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res, 
00931                        const char *expr, const char **attrs)
00932 {
00933         return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, 
00934                              expr, attrs, res);
00935 }
00936 
00937 /**
00938  * Do a search on a specific DistinguishedName
00939  * @param ads connection to ads server
00940  * @param res ** which will contain results - free res* with ads_msgfree()
00941  * @param dn DistinguishName to search
00942  * @param attrs Attributes to retrieve
00943  * @return status of search
00944  **/
00945  ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res, 
00946                           const char *dn, const char **attrs)
00947 {
00948         return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
00949                              attrs, res);
00950 }
00951 
00952 /**
00953  * Free up memory from a ads_search
00954  * @param ads connection to ads server
00955  * @param msg Search results to free
00956  **/
00957  void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
00958 {
00959         if (!msg) return;
00960         ldap_msgfree(msg);
00961 }
00962 
00963 /**
00964  * Free up memory from various ads requests
00965  * @param ads connection to ads server
00966  * @param mem Area to free
00967  **/
00968 void ads_memfree(ADS_STRUCT *ads, void *mem)
00969 {
00970         SAFE_FREE(mem);
00971 }
00972 
00973 /**
00974  * Get a dn from search results
00975  * @param ads connection to ads server
00976  * @param msg Search result
00977  * @return dn string
00978  **/
00979  char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
00980 {
00981         char *utf8_dn, *unix_dn;
00982 
00983         utf8_dn = ldap_get_dn(ads->ld, msg);
00984 
00985         if (!utf8_dn) {
00986                 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
00987                 return NULL;
00988         }
00989 
00990         if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
00991                 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
00992                         utf8_dn ));
00993                 return NULL;
00994         }
00995         ldap_memfree(utf8_dn);
00996         return unix_dn;
00997 }
00998 
00999 /**
01000  * Get the parent from a dn
01001  * @param dn the dn to return the parent from
01002  * @return parent dn string
01003  **/
01004 char *ads_parent_dn(const char *dn)
01005 {
01006         char *p;
01007 
01008         if (dn == NULL) {
01009                 return NULL;
01010         }
01011 
01012         p = strchr(dn, ',');
01013 
01014         if (p == NULL) {
01015                 return NULL;
01016         }
01017 
01018         return p+1;
01019 }
01020 
01021 /**
01022  * Find a machine account given a hostname
01023  * @param ads connection to ads server
01024  * @param res ** which will contain results - free res* with ads_msgfree()
01025  * @param host Hostname to search for
01026  * @return status of search
01027  **/
01028  ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
01029                                   const char *machine)
01030 {
01031         ADS_STATUS status;
01032         char *expr;
01033         const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
01034 
01035         *res = NULL;
01036 
01037         /* the easiest way to find a machine account anywhere in the tree
01038            is to look for hostname$ */
01039         if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
01040                 DEBUG(1, ("asprintf failed!\n"));
01041                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
01042         }
01043         
01044         status = ads_search(ads, res, expr, attrs);
01045         SAFE_FREE(expr);
01046         return status;
01047 }
01048 
01049 /**
01050  * Initialize a list of mods to be used in a modify request
01051  * @param ctx An initialized TALLOC_CTX
01052  * @return allocated ADS_MODLIST
01053  **/
01054 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
01055 {
01056 #define ADS_MODLIST_ALLOC_SIZE 10
01057         LDAPMod **mods;
01058         
01059         if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
01060                 /* -1 is safety to make sure we don't go over the end.
01061                    need to reset it to NULL before doing ldap modify */
01062                 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
01063         
01064         return (ADS_MODLIST)mods;
01065 }
01066 
01067 
01068 /*
01069   add an attribute to the list, with values list already constructed
01070 */
01071 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
01072                                   int mod_op, const char *name, 
01073                                   const void *_invals)
01074 {
01075         const void **invals = (const void **)_invals;
01076         int curmod;
01077         LDAPMod **modlist = (LDAPMod **) *mods;
01078         struct berval **ber_values = NULL;
01079         char **char_values = NULL;
01080 
01081         if (!invals) {
01082                 mod_op = LDAP_MOD_DELETE;
01083         } else {
01084                 if (mod_op & LDAP_MOD_BVALUES)
01085                         ber_values = ads_dup_values(ctx, 
01086                                                 (const struct berval **)invals);
01087                 else
01088                         char_values = ads_push_strvals(ctx, 
01089                                                   (const char **) invals);
01090         }
01091 
01092         /* find the first empty slot */
01093         for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
01094              curmod++);
01095         if (modlist[curmod] == (LDAPMod *) -1) {
01096                 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
01097                                 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
01098                         return ADS_ERROR(LDAP_NO_MEMORY);
01099                 memset(&modlist[curmod], 0, 
01100                        ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
01101                 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
01102                 *mods = (ADS_MODLIST)modlist;
01103         }
01104                 
01105         if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
01106                 return ADS_ERROR(LDAP_NO_MEMORY);
01107         modlist[curmod]->mod_type = talloc_strdup(ctx, name);
01108         if (mod_op & LDAP_MOD_BVALUES) {
01109                 modlist[curmod]->mod_bvalues = ber_values;
01110         } else if (mod_op & LDAP_MOD_DELETE) {
01111                 modlist[curmod]->mod_values = NULL;
01112         } else {
01113                 modlist[curmod]->mod_values = char_values;
01114         }
01115 
01116         modlist[curmod]->mod_op = mod_op;
01117         return ADS_ERROR(LDAP_SUCCESS);
01118 }
01119 
01120 /**
01121  * Add a single string value to a mod list
01122  * @param ctx An initialized TALLOC_CTX
01123  * @param mods An initialized ADS_MODLIST
01124  * @param name The attribute name to add
01125  * @param val The value to add - NULL means DELETE
01126  * @return ADS STATUS indicating success of add
01127  **/
01128 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
01129                        const char *name, const char *val)
01130 {
01131         const char *values[2];
01132 
01133         values[0] = val;
01134         values[1] = NULL;
01135 
01136         if (!val)
01137                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
01138         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
01139 }
01140 
01141 /**
01142  * Add an array of string values to a mod list
01143  * @param ctx An initialized TALLOC_CTX
01144  * @param mods An initialized ADS_MODLIST
01145  * @param name The attribute name to add
01146  * @param vals The array of string values to add - NULL means DELETE
01147  * @return ADS STATUS indicating success of add
01148  **/
01149 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
01150                            const char *name, const char **vals)
01151 {
01152         if (!vals)
01153                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
01154         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, 
01155                                name, (const void **) vals);
01156 }
01157 
01158 #if 0
01159 /**
01160  * Add a single ber-encoded value to a mod list
01161  * @param ctx An initialized TALLOC_CTX
01162  * @param mods An initialized ADS_MODLIST
01163  * @param name The attribute name to add
01164  * @param val The value to add - NULL means DELETE
01165  * @return ADS STATUS indicating success of add
01166  **/
01167 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods, 
01168                               const char *name, const struct berval *val)
01169 {
01170         const struct berval *values[2];
01171 
01172         values[0] = val;
01173         values[1] = NULL;
01174         if (!val)
01175                 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
01176         return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
01177                                name, (const void **) values);
01178 }
01179 #endif
01180 
01181 /**
01182  * Perform an ldap modify
01183  * @param ads connection to ads server
01184  * @param mod_dn DistinguishedName to modify
01185  * @param mods list of modifications to perform
01186  * @return status of modify
01187  **/
01188 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
01189 {
01190         int ret,i;
01191         char *utf8_dn = NULL;
01192         /* 
01193            this control is needed to modify that contains a currently 
01194            non-existent attribute (but allowable for the object) to run
01195         */
01196         LDAPControl PermitModify = {
01197                 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
01198                 {0, NULL},
01199                 (char) 1};
01200         LDAPControl *controls[2];
01201 
01202         controls[0] = &PermitModify;
01203         controls[1] = NULL;
01204 
01205         if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
01206                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
01207         }
01208 
01209         /* find the end of the list, marked by NULL or -1 */
01210         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
01211         /* make sure the end of the list is NULL */
01212         mods[i] = NULL;
01213         ret = ldap_modify_ext_s(ads->ld, utf8_dn,
01214                                 (LDAPMod **) mods, controls, NULL);
01215         SAFE_FREE(utf8_dn);
01216         return ADS_ERROR(ret);
01217 }
01218 
01219 /**
01220  * Perform an ldap add
01221  * @param ads connection to ads server
01222  * @param new_dn DistinguishedName to add
01223  * @param mods list of attributes and values for DN
01224  * @return status of add
01225  **/
01226 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
01227 {
01228         int ret, i;
01229         char *utf8_dn = NULL;
01230 
01231         if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
01232                 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
01233                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
01234         }
01235         
01236         /* find the end of the list, marked by NULL or -1 */
01237         for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
01238         /* make sure the end of the list is NULL */
01239         mods[i] = NULL;
01240 
01241         ret = ldap_add_s(ads->ld, utf8_dn, (LDAPMod**)mods);
01242         SAFE_FREE(utf8_dn);
01243         return ADS_ERROR(ret);
01244 }
01245 
01246 /**
01247  * Delete a DistinguishedName
01248  * @param ads connection to ads server
01249  * @param new_dn DistinguishedName to delete
01250  * @return status of delete
01251  **/
01252 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
01253 {
01254         int ret;
01255         char *utf8_dn = NULL;
01256         if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
01257                 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
01258                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
01259         }
01260         
01261         ret = ldap_delete_s(ads->ld, utf8_dn);
01262         SAFE_FREE(utf8_dn);
01263         return ADS_ERROR(ret);
01264 }
01265 
01266 /**
01267  * Build an org unit string
01268  *  if org unit is Computers or blank then assume a container, otherwise
01269  *  assume a / separated list of organisational units.
01270  * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
01271  * @param ads connection to ads server
01272  * @param org_unit Organizational unit
01273  * @return org unit string - caller must free
01274  **/
01275 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
01276 {
01277         char *ret = NULL;
01278 
01279         if (!org_unit || !*org_unit) {
01280 
01281                 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
01282 
01283                 /* samba4 might not yet respond to a wellknownobject-query */
01284                 return ret ? ret : SMB_STRDUP("cn=Computers");
01285         }
01286         
01287         if (strequal(org_unit, "Computers")) {
01288                 return SMB_STRDUP("cn=Computers");
01289         }
01290 
01291         /* jmcd: removed "\\" from the separation chars, because it is
01292            needed as an escape for chars like '#' which are valid in an
01293            OU name */
01294         return ads_build_path(org_unit, "/", "ou=", 1);
01295 }
01296 
01297 /**
01298  * Get a org unit string for a well-known GUID
01299  * @param ads connection to ads server
01300  * @param wknguid Well known GUID
01301  * @return org unit string - caller must free
01302  **/
01303 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
01304 {
01305         ADS_STATUS status;
01306         LDAPMessage *res = NULL;
01307         char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
01308                 **bind_dn_exp = NULL;
01309         const char *attrs[] = {"distinguishedName", NULL};
01310         int new_ln, wkn_ln, bind_ln, i;
01311 
01312         if (wknguid == NULL) {
01313                 return NULL;
01314         }
01315 
01316         if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
01317                 DEBUG(1, ("asprintf failed!\n"));
01318                 return NULL;
01319         }
01320 
01321         status = ads_search_dn(ads, &res, base, attrs);
01322         if (!ADS_ERR_OK(status)) {
01323                 DEBUG(1,("Failed while searching for: %s\n", base));
01324                 goto out;
01325         }
01326 
01327         if (ads_count_replies(ads, res) != 1) {
01328                 goto out;
01329         }
01330 
01331         /* substitute the bind-path from the well-known-guid-search result */
01332         wkn_dn = ads_get_dn(ads, res);
01333         if (!wkn_dn) {
01334                 goto out;
01335         }
01336 
01337         wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
01338         if (!wkn_dn_exp) {
01339                 goto out;
01340         }
01341 
01342         bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
01343         if (!bind_dn_exp) {
01344                 goto out;
01345         }
01346 
01347         for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
01348                 ;
01349         for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
01350                 ;
01351 
01352         new_ln = wkn_ln - bind_ln;
01353 
01354         ret = SMB_STRDUP(wkn_dn_exp[0]);
01355         if (!ret) {
01356                 goto out;
01357         }
01358 
01359         for (i=1; i < new_ln; i++) {
01360                 char *s = NULL;
01361                 
01362                 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
01363                         SAFE_FREE(ret);
01364                         goto out;
01365                 }
01366 
01367                 SAFE_FREE(ret);
01368                 ret = SMB_STRDUP(s);
01369                 free(s);
01370                 if (!ret) {
01371                         goto out;
01372                 }
01373         }
01374 
01375  out:
01376         SAFE_FREE(base);
01377         ads_msgfree(ads, res);
01378         ads_memfree(ads, wkn_dn);
01379         if (wkn_dn_exp) {
01380                 ldap_value_free(wkn_dn_exp);
01381         }
01382         if (bind_dn_exp) {
01383                 ldap_value_free(bind_dn_exp);
01384         }
01385 
01386         return ret;
01387 }
01388 
01389 /**
01390  * Adds (appends) an item to an attribute array, rather then
01391  * replacing the whole list
01392  * @param ctx An initialized TALLOC_CTX
01393  * @param mods An initialized ADS_MODLIST
01394  * @param name name of the ldap attribute to append to
01395  * @param vals an array of values to add
01396  * @return status of addition
01397  **/
01398 
01399 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
01400                                 const char *name, const char **vals)
01401 {
01402         return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
01403                                (const void *) vals);
01404 }
01405 
01406 /**
01407  * Determines the computer account's current KVNO via an LDAP lookup
01408  * @param ads An initialized ADS_STRUCT
01409  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
01410  * @return the kvno for the computer account, or -1 in case of a failure.
01411  **/
01412 
01413 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
01414 {
01415         LDAPMessage *res = NULL;
01416         uint32 kvno = (uint32)-1;      /* -1 indicates a failure */
01417         char *filter;
01418         const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
01419         char *dn_string = NULL;
01420         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
01421 
01422         DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
01423         if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
01424                 return kvno;
01425         }
01426         ret = ads_search(ads, &res, filter, attrs);
01427         SAFE_FREE(filter);
01428         if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
01429                 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
01430                 ads_msgfree(ads, res);
01431                 return kvno;
01432         }
01433 
01434         dn_string = ads_get_dn(ads, res);
01435         if (!dn_string) {
01436                 DEBUG(0,("ads_get_kvno: out of memory.\n"));
01437                 ads_msgfree(ads, res);
01438                 return kvno;
01439         }
01440         DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
01441         ads_memfree(ads, dn_string);
01442 
01443         /* ---------------------------------------------------------
01444          * 0 is returned as a default KVNO from this point on...
01445          * This is done because Windows 2000 does not support key
01446          * version numbers.  Chances are that a failure in the next
01447          * step is simply due to Windows 2000 being used for a
01448          * domain controller. */
01449         kvno = 0;
01450 
01451         if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
01452                 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
01453                 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
01454                 ads_msgfree(ads, res);
01455                 return kvno;
01456         }
01457 
01458         /* Success */
01459         DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
01460         ads_msgfree(ads, res);
01461         return kvno;
01462 }
01463 
01464 /**
01465  * This clears out all registered spn's for a given hostname
01466  * @param ads An initilaized ADS_STRUCT
01467  * @param machine_name the NetBIOS name of the computer.
01468  * @return 0 upon success, non-zero otherwise.
01469  **/
01470 
01471 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
01472 {
01473         TALLOC_CTX *ctx;
01474         LDAPMessage *res = NULL;
01475         ADS_MODLIST mods;
01476         const char *servicePrincipalName[1] = {NULL};
01477         ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
01478         char *dn_string = NULL;
01479 
01480         ret = ads_find_machine_acct(ads, &res, machine_name);
01481         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
01482                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
01483                 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
01484                 ads_msgfree(ads, res);
01485                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
01486         }
01487 
01488         DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
01489         ctx = talloc_init("ads_clear_service_principal_names");
01490         if (!ctx) {
01491                 ads_msgfree(ads, res);
01492                 return ADS_ERROR(LDAP_NO_MEMORY);
01493         }
01494 
01495         if (!(mods = ads_init_mods(ctx))) {
01496                 talloc_destroy(ctx);
01497                 ads_msgfree(ads, res);
01498                 return ADS_ERROR(LDAP_NO_MEMORY);
01499         }
01500         ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
01501         if (!ADS_ERR_OK(ret)) {
01502                 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
01503                 ads_msgfree(ads, res);
01504                 talloc_destroy(ctx);
01505                 return ret;
01506         }
01507         dn_string = ads_get_dn(ads, res);
01508         if (!dn_string) {
01509                 talloc_destroy(ctx);
01510                 ads_msgfree(ads, res);
01511                 return ADS_ERROR(LDAP_NO_MEMORY);
01512         }
01513         ret = ads_gen_mod(ads, dn_string, mods);
01514         ads_memfree(ads,dn_string);
01515         if (!ADS_ERR_OK(ret)) {
01516                 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
01517                         machine_name));
01518                 ads_msgfree(ads, res);
01519                 talloc_destroy(ctx);
01520                 return ret;
01521         }
01522 
01523         ads_msgfree(ads, res);
01524         talloc_destroy(ctx);
01525         return ret;
01526 }
01527 
01528 /**
01529  * This adds a service principal name to an existing computer account
01530  * (found by hostname) in AD.
01531  * @param ads An initialized ADS_STRUCT
01532  * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
01533  * @param my_fqdn The fully qualified DNS name of the machine
01534  * @param spn A string of the service principal to add, i.e. 'host'
01535  * @return 0 upon sucess, or non-zero if a failure occurs
01536  **/
01537 
01538 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, 
01539                                           const char *my_fqdn, const char *spn)
01540 {
01541         ADS_STATUS ret;
01542         TALLOC_CTX *ctx;
01543         LDAPMessage *res = NULL;
01544         char *psp1, *psp2;
01545         ADS_MODLIST mods;
01546         char *dn_string = NULL;
01547         const char *servicePrincipalName[3] = {NULL, NULL, NULL};
01548 
01549         ret = ads_find_machine_acct(ads, &res, machine_name);
01550         if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
01551                 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
01552                         machine_name));
01553                 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
01554                         spn, machine_name, ads->config.realm));
01555                 ads_msgfree(ads, res);
01556                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
01557         }
01558 
01559         DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
01560         if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
01561                 ads_msgfree(ads, res);
01562                 return ADS_ERROR(LDAP_NO_MEMORY);
01563         }
01564 
01565         /* add short name spn */
01566         
01567         if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
01568                 talloc_destroy(ctx);
01569                 ads_msgfree(ads, res);
01570                 return ADS_ERROR(LDAP_NO_MEMORY);
01571         }
01572         strupper_m(psp1);
01573         strlower_m(&psp1[strlen(spn)]);
01574         servicePrincipalName[0] = psp1;
01575         
01576         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
01577                 psp1, machine_name));
01578 
01579 
01580         /* add fully qualified spn */
01581         
01582         if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
01583                 ret = ADS_ERROR(LDAP_NO_MEMORY);
01584                 goto out;
01585         }
01586         strupper_m(psp2);
01587         strlower_m(&psp2[strlen(spn)]);
01588         servicePrincipalName[1] = psp2;
01589 
01590         DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", 
01591                 psp2, machine_name));
01592 
01593         if ( (mods = ads_init_mods(ctx)) == NULL ) {
01594                 ret = ADS_ERROR(LDAP_NO_MEMORY);
01595                 goto out;
01596         }
01597         
01598         ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
01599         if (!ADS_ERR_OK(ret)) {
01600                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
01601                 goto out;
01602         }
01603         
01604         if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
01605                 ret = ADS_ERROR(LDAP_NO_MEMORY);
01606                 goto out;
01607         }
01608         
01609         ret = ads_gen_mod(ads, dn_string, mods);
01610         ads_memfree(ads,dn_string);
01611         if (!ADS_ERR_OK(ret)) {
01612                 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
01613                 goto out;
01614         }
01615 
01616  out:
01617         TALLOC_FREE( ctx );
01618         ads_msgfree(ads, res);
01619         return ret;
01620 }
01621 
01622 /**
01623  * adds a machine account to the ADS server
01624  * @param ads An intialized ADS_STRUCT
01625  * @param machine_name - the NetBIOS machine name of this account.
01626  * @param account_type A number indicating the type of account to create
01627  * @param org_unit The LDAP path in which to place this account
01628  * @return 0 upon success, or non-zero otherwise
01629 **/
01630 
01631 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name, 
01632                                    const char *org_unit)
01633 {
01634         ADS_STATUS ret;
01635         char *samAccountName, *controlstr;
01636         TALLOC_CTX *ctx;
01637         ADS_MODLIST mods;
01638         char *machine_escaped = NULL;
01639         char *new_dn;
01640         const char *objectClass[] = {"top", "person", "organizationalPerson",
01641                                      "user", "computer", NULL};
01642         LDAPMessage *res = NULL;
01643         uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
01644                                 UF_DONT_EXPIRE_PASSWD |\
01645                                 UF_ACCOUNTDISABLE );
01646                               
01647         if (!(ctx = talloc_init("ads_add_machine_acct")))
01648                 return ADS_ERROR(LDAP_NO_MEMORY);
01649 
01650         ret = ADS_ERROR(LDAP_NO_MEMORY);
01651 
01652         machine_escaped = escape_rdn_val_string_alloc(machine_name);
01653         if (!machine_escaped) {
01654                 goto done;
01655         }
01656 
01657         new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
01658         samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
01659 
01660         if ( !new_dn || !samAccountName ) {
01661                 goto done;
01662         }
01663         
01664 #ifndef ENCTYPE_ARCFOUR_HMAC
01665         acct_control |= UF_USE_DES_KEY_ONLY;
01666 #endif
01667 
01668         if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
01669                 goto done;
01670         }
01671 
01672         if (!(mods = ads_init_mods(ctx))) {
01673                 goto done;
01674         }
01675         
01676         ads_mod_str(ctx, &mods, "cn", machine_name);
01677         ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
01678         ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
01679         ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
01680 
01681         ret = ads_gen_add(ads, new_dn, mods);
01682 
01683 done:
01684         SAFE_FREE(machine_escaped);
01685         ads_msgfree(ads, res);
01686         talloc_destroy(ctx);
01687         
01688         return ret;
01689 }
01690 
01691 /*
01692   dump a binary result from ldap
01693 */
01694 static void dump_binary(const char *field, struct berval **values)
01695 {
01696         int i, j;
01697         for (i=0; values[i]; i++) {
01698                 printf("%s: ", field);
01699                 for (j=0; j<values[i]->bv_len; j++) {
01700                         printf("%02X", (unsigned char)values[i]->bv_val[j]);
01701                 }
01702                 printf("\n");
01703         }
01704 }
01705 
01706 static void dump_guid(const char *field, struct berval **values)
01707 {
01708         int i;
01709         UUID_FLAT guid;
01710         for (i=0; values[i]; i++) {
01711                 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
01712                 printf("%s: %s\n", field, 
01713                        smb_uuid_string_static(smb_uuid_unpack_static(guid)));
01714         }
01715 }
01716 
01717 /*
01718   dump a sid result from ldap
01719 */
01720 static void dump_sid(const char *field, struct berval **values)
01721 {
01722         int i;
01723         for (i=0; values[i]; i++) {
01724                 DOM_SID sid;
01725                 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
01726                 printf("%s: %s\n", field, sid_string_static(&sid));
01727         }
01728 }
01729 
01730 /*
01731   dump ntSecurityDescriptor
01732 */
01733 static void dump_sd(const char *filed, struct berval **values)
01734 {
01735         prs_struct ps;
01736         
01737         SEC_DESC   *psd = 0;
01738         TALLOC_CTX *ctx = 0;
01739 
01740         if (!(ctx = talloc_init("sec_io_desc")))
01741                 return;
01742 
01743         /* prepare data */
01744         prs_init(&ps, values[0]->bv_len, ctx, UNMARSHALL);
01745         prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
01746         prs_set_offset(&ps,0);
01747 
01748         /* parse secdesc */
01749         if (!sec_io_desc("sd", &psd, &ps, 1)) {
01750                 prs_mem_free(&ps);
01751                 talloc_destroy(ctx);
01752                 return;
01753         }
01754         if (psd) ads_disp_sd(psd);
01755 
01756         prs_mem_free(&ps);
01757         talloc_destroy(ctx);
01758 }
01759 
01760 /*
01761   dump a string result from ldap
01762 */
01763 static void dump_string(const char *field, char **values)
01764 {
01765         int i;
01766         for (i=0; values[i]; i++) {
01767                 printf("%s: %s\n", field, values[i]);
01768         }
01769 }
01770 
01771 /*
01772   dump a field from LDAP on stdout
01773   used for debugging
01774 */
01775 
01776 static BOOL ads_dump_field(char *field, void **values, void *data_area)
01777 {
01778         const struct {
01779                 const char *name;
01780                 BOOL string;
01781                 void (*handler)(const char *, struct berval **);
01782         } handlers[] = {
01783                 {"objectGUID", False, dump_guid},
01784                 {"netbootGUID", False, dump_guid},
01785                 {"nTSecurityDescriptor", False, dump_sd},
01786                 {"dnsRecord", False, dump_binary},
01787                 {"objectSid", False, dump_sid},
01788                 {"tokenGroups", False, dump_sid},
01789                 {"tokenGroupsNoGCAcceptable", False, dump_sid},
01790                 {"tokengroupsGlobalandUniversal", False, dump_sid},
01791                 {"mS-DS-CreatorSID", False, dump_sid},
01792                 {NULL, True, NULL}
01793         };
01794         int i;
01795 
01796         if (!field) { /* must be end of an entry */
01797                 printf("\n");
01798                 return False;
01799         }
01800 
01801         for (i=0; handlers[i].name; i++) {
01802                 if (StrCaseCmp(handlers[i].name, field) == 0) {
01803                         if (!values) /* first time, indicate string or not */
01804                                 return handlers[i].string;
01805                         handlers[i].handler(field, (struct berval **) values);
01806                         break;
01807                 }
01808         }
01809         if (!handlers[i].name) {
01810                 if (!values) /* first time, indicate string conversion */
01811                         return True;
01812                 dump_string(field, (char **)values);
01813         }
01814         return False;
01815 }
01816 
01817 /**
01818  * Dump a result from LDAP on stdout
01819  *  used for debugging
01820  * @param ads connection to ads server
01821  * @param res Results to dump
01822  **/
01823 
01824  void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
01825 {
01826         ads_process_results(ads, res, ads_dump_field, NULL);
01827 }
01828 
01829 /**
01830  * Walk through results, calling a function for each entry found.
01831  *  The function receives a field name, a berval * array of values,
01832  *  and a data area passed through from the start.  The function is
01833  *  called once with null for field and values at the end of each
01834  *  entry.
01835  * @param ads connection to ads server
01836  * @param res Results to process
01837  * @param fn Function for processing each result
01838  * @param data_area user-defined area to pass to function
01839  **/
01840  void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
01841                           BOOL(*fn)(char *, void **, void *),
01842                           void *data_area)
01843 {
01844         LDAPMessage *msg;
01845         TALLOC_CTX *ctx;
01846 
01847         if (!(ctx = talloc_init("ads_process_results")))
01848                 return;
01849 
01850         for (msg = ads_first_entry(ads, res); msg; 
01851              msg = ads_next_entry(ads, msg)) {
01852                 char *utf8_field;
01853                 BerElement *b;
01854         
01855                 for (utf8_field=ldap_first_attribute(ads->ld,
01856                                                      (LDAPMessage *)msg,&b); 
01857                      utf8_field;
01858                      utf8_field=ldap_next_attribute(ads->ld,
01859                                                     (LDAPMessage *)msg,b)) {
01860                         struct berval **ber_vals;
01861                         char **str_vals, **utf8_vals;
01862                         char *field;
01863                         BOOL string; 
01864 
01865                         pull_utf8_talloc(ctx, &field, utf8_field);
01866                         string = fn(field, NULL, data_area);
01867 
01868                         if (string) {
01869                                 utf8_vals = ldap_get_values(ads->ld,
01870                                                  (LDAPMessage *)msg, field);
01871                                 str_vals = ads_pull_strvals(ctx, 
01872                                                   (const char **) utf8_vals);
01873                                 fn(field, (void **) str_vals, data_area);
01874                                 ldap_value_free(utf8_vals);
01875                         } else {
01876                                 ber_vals = ldap_get_values_len(ads->ld, 
01877                                                  (LDAPMessage *)msg, field);
01878                                 fn(field, (void **) ber_vals, data_area);
01879 
01880                                 ldap_value_free_len(ber_vals);
01881                         }
01882                         ldap_memfree(utf8_field);
01883                 }
01884                 ber_free(b, 0);
01885                 talloc_free_children(ctx);
01886                 fn(NULL, NULL, data_area); /* completed an entry */
01887 
01888         }
01889         talloc_destroy(ctx);
01890 }
01891 
01892 /**
01893  * count how many replies are in a LDAPMessage
01894  * @param ads connection to ads server
01895  * @param res Results to count
01896  * @return number of replies
01897  **/
01898 int ads_count_replies(ADS_STRUCT *ads, void *res)
01899 {
01900         return ldap_count_entries(ads->ld, (LDAPMessage *)res);
01901 }
01902 
01903 /**
01904  * pull the first entry from a ADS result
01905  * @param ads connection to ads server
01906  * @param res Results of search
01907  * @return first entry from result
01908  **/
01909  LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
01910 {
01911         return ldap_first_entry(ads->ld, res);
01912 }
01913 
01914 /**
01915  * pull the next entry from a ADS result
01916  * @param ads connection to ads server
01917  * @param res Results of search
01918  * @return next entry from result
01919  **/
01920  LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
01921 {
01922         return ldap_next_entry(ads->ld, res);
01923 }
01924 
01925 /**
01926  * pull the first message from a ADS result
01927  * @param ads connection to ads server
01928  * @param res Results of search
01929  * @return first message from result
01930  **/
01931  LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
01932 {
01933         return ldap_first_message(ads->ld, res);
01934 }
01935 
01936 /**
01937  * pull the next message from a ADS result
01938  * @param ads connection to ads server
01939  * @param res Results of search
01940  * @return next message from result
01941  **/
01942  LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
01943 {
01944         return ldap_next_message(ads->ld, res);
01945 }
01946 
01947 /**
01948  * pull a single string from a ADS result
01949  * @param ads connection to ads server
01950  * @param mem_ctx TALLOC_CTX to use for allocating result string
01951  * @param msg Results of search
01952  * @param field Attribute to retrieve
01953  * @return Result string in talloc context
01954  **/
01955  char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
01956                        const char *field)
01957 {
01958         char **values;
01959         char *ret = NULL;
01960         char *ux_string;
01961         size_t rc;
01962 
01963         values = ldap_get_values(ads->ld, msg, field);
01964         if (!values)
01965                 return NULL;
01966         
01967         if (values[0]) {
01968                 rc = pull_utf8_talloc(mem_ctx, &ux_string, 
01969                                       values[0]);
01970                 if (rc != (size_t)-1)
01971                         ret = ux_string;
01972                 
01973         }
01974         ldap_value_free(values);
01975         return ret;
01976 }
01977 
01978 /**
01979  * pull an array of strings from a ADS result
01980  * @param ads connection to ads server
01981  * @param mem_ctx TALLOC_CTX to use for allocating result string
01982  * @param msg Results of search
01983  * @param field Attribute to retrieve
01984  * @return Result strings in talloc context
01985  **/
01986  char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
01987                          LDAPMessage *msg, const char *field,
01988                          size_t *num_values)
01989 {
01990         char **values;
01991         char **ret = NULL;
01992         int i;
01993 
01994         values = ldap_get_values(ads->ld, msg, field);
01995         if (!values)
01996                 return NULL;
01997 
01998         *num_values = ldap_count_values(values);
01999 
02000         ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
02001         if (!ret) {
02002                 ldap_value_free(values);
02003                 return NULL;
02004         }
02005 
02006         for (i=0;i<*num_values;i++) {
02007                 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
02008                         ldap_value_free(values);
02009                         return NULL;
02010                 }
02011         }
02012         ret[i] = NULL;
02013 
02014         ldap_value_free(values);
02015         return ret;
02016 }
02017 
02018 /**
02019  * pull an array of strings from a ADS result 
02020  *  (handle large multivalue attributes with range retrieval)
02021  * @param ads connection to ads server
02022  * @param mem_ctx TALLOC_CTX to use for allocating result string
02023  * @param msg Results of search
02024  * @param field Attribute to retrieve
02025  * @param current_strings strings returned by a previous call to this function
02026  * @param next_attribute The next query should ask for this attribute
02027  * @param num_values How many values did we get this time?
02028  * @param more_values Are there more values to get?
02029  * @return Result strings in talloc context
02030  **/
02031  char **ads_pull_strings_range(ADS_STRUCT *ads, 
02032                                TALLOC_CTX *mem_ctx,
02033                                LDAPMessage *msg, const char *field,
02034                                char **current_strings,
02035                                const char **next_attribute,
02036                                size_t *num_strings,
02037                                BOOL *more_strings)
02038 {
02039         char *attr;
02040         char *expected_range_attrib, *range_attr;
02041         BerElement *ptr = NULL;
02042         char **strings;
02043         char **new_strings;
02044         size_t num_new_strings;
02045         unsigned long int range_start;
02046         unsigned long int range_end;
02047         
02048         /* we might have been given the whole lot anyway */
02049         if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
02050                 *more_strings = False;
02051                 return strings;
02052         }
02053 
02054         expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
02055 
02056         /* look for Range result */
02057         for (attr = ldap_first_attribute(ads->ld, (LDAPMessage *)msg, &ptr); 
02058              attr; 
02059              attr = ldap_next_attribute(ads->ld, (LDAPMessage *)msg, ptr)) {
02060                 /* we ignore the fact that this is utf8, as all attributes are ascii... */
02061                 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
02062                         range_attr = attr;
02063                         break;
02064                 }
02065                 ldap_memfree(attr);
02066         }
02067         if (!attr) {
02068                 ber_free(ptr, 0);
02069                 /* nothing here - this field is just empty */
02070                 *more_strings = False;
02071                 return NULL;
02072         }
02073         
02074         if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu", 
02075                    &range_start, &range_end) == 2) {
02076                 *more_strings = True;
02077         } else {
02078                 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*", 
02079                            &range_start) == 1) {
02080                         *more_strings = False;
02081                 } else {
02082                         DEBUG(1, ("ads_pull_strings_range:  Cannot parse Range attriubte (%s)\n", 
02083                                   range_attr));
02084                         ldap_memfree(range_attr);
02085                         *more_strings = False;
02086                         return NULL;
02087                 }
02088         }
02089 
02090         if ((*num_strings) != range_start) {
02091                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
02092                           " - aborting range retreival\n",
02093                           range_attr, (unsigned int)(*num_strings) + 1, range_start));
02094                 ldap_memfree(range_attr);
02095                 *more_strings = False;
02096                 return NULL;
02097         }
02098 
02099         new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
02100         
02101         if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
02102                 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
02103                           "strings in this bunch, but we only got %lu - aborting range retreival\n",
02104                           range_attr, (unsigned long int)range_end - range_start + 1, 
02105                           (unsigned long int)num_new_strings));
02106                 ldap_memfree(range_attr);
02107                 *more_strings = False;
02108                 return NULL;
02109         }
02110 
02111         strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
02112                                  *num_strings + num_new_strings);
02113         
02114         if (strings == NULL) {
02115                 ldap_memfree(range_attr);
02116                 *more_strings = False;
02117                 return NULL;
02118         }
02119         
02120         if (new_strings && num_new_strings) {
02121                 memcpy(&strings[*num_strings], new_strings,
02122                        sizeof(*new_strings) * num_new_strings);
02123         }
02124 
02125         (*num_strings) += num_new_strings;
02126 
02127         if (*more_strings) {
02128                 *next_attribute = talloc_asprintf(mem_ctx,
02129                                                   "%s;range=%d-*", 
02130                                                   field,
02131                                                   (int)*num_strings);
02132                 
02133                 if (!*next_attribute) {
02134                         DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
02135                         ldap_memfree(range_attr);
02136                         *more_strings = False;
02137                         return NULL;
02138                 }
02139         }
02140 
02141         ldap_memfree(range_attr);
02142 
02143         return strings;
02144 }
02145 
02146 /**
02147  * pull a single uint32 from a ADS result
02148  * @param ads connection to ads server
02149  * @param msg Results of search
02150  * @param field Attribute to retrieve
02151  * @param v Pointer to int to store result
02152  * @return boolean inidicating success
02153 */
02154  BOOL ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
02155                       uint32 *v)
02156 {
02157         char **values;
02158 
02159         values = ldap_get_values(ads->ld, msg, field);
02160         if (!values)
02161                 return False;
02162         if (!values[0]) {
02163                 ldap_value_free(values);
02164                 return False;
02165         }
02166 
02167         *v = atoi(values[0]);
02168         ldap_value_free(values);
02169         return True;
02170 }
02171 
02172 /**
02173  * pull a single objectGUID from an ADS result
02174  * @param ads connection to ADS server
02175  * @param msg results of search
02176  * @param guid 37-byte area to receive text guid
02177  * @return boolean indicating success
02178  **/
02179  BOOL ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
02180 {
02181         char **values;
02182         UUID_FLAT flat_guid;
02183 
02184         values = ldap_get_values(ads->ld, msg, "objectGUID");
02185         if (!values)
02186                 return False;
02187         
02188         if (values[0]) {
02189                 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
02190                 smb_uuid_unpack(flat_guid, guid);
02191                 ldap_value_free(values);
02192                 return True;
02193         }
02194         ldap_value_free(values);
02195         return False;
02196 
02197 }
02198 
02199 
02200 /**
02201  * pull a single DOM_SID from a ADS result
02202  * @param ads connection to ads server
02203  * @param msg Results of search
02204  * @param field Attribute to retrieve
02205  * @param sid Pointer to sid to store result
02206  * @return boolean inidicating success
02207 */
02208  BOOL ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
02209                    DOM_SID *sid)
02210 {
02211         struct berval **values;
02212         BOOL ret = False;
02213 
02214         values = ldap_get_values_len(ads->ld, msg, field);
02215 
02216         if (!values)
02217                 return False;
02218 
02219         if (values[0])
02220                 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
02221         
02222         ldap_value_free_len(values);
02223         return ret;
02224 }
02225 
02226 /**
02227  * pull an array of DOM_SIDs from a ADS result
02228  * @param ads connection to ads server
02229  * @param mem_ctx TALLOC_CTX for allocating sid array
02230  * @param msg Results of search
02231  * @param field Attribute to retrieve
02232  * @param sids pointer to sid array to allocate
02233  * @return the count of SIDs pulled
02234  **/
02235  int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
02236                    LDAPMessage *msg, const char *field, DOM_SID **sids)
02237 {
02238         struct berval **values;
02239         BOOL ret;
02240         int count, i;
02241 
02242         values = ldap_get_values_len(ads->ld, msg, field);
02243 
02244         if (!values)
02245                 return 0;
02246 
02247         for (i=0; values[i]; i++)
02248                 /* nop */ ;
02249 
02250         if (i) {
02251                 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
02252                 if (!(*sids)) {
02253                         ldap_value_free_len(values);
02254                         return 0;
02255                 }
02256         } else {
02257                 (*sids) = NULL;
02258         }
02259 
02260         count = 0;
02261         for (i=0; values[i]; i++) {
02262                 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
02263                 if (ret) {
02264                         fstring sid;
02265                         DEBUG(10, ("pulling SID: %s\n", sid_to_string(sid, &(*sids)[count])));
02266                         count++;
02267                 }
02268         }
02269         
02270         ldap_value_free_len(values);
02271         return count;
02272 }
02273 
02274 /**
02275  * pull a SEC_DESC from a ADS result
02276  * @param ads connection to ads server
02277  * @param mem_ctx TALLOC_CTX for allocating sid array
02278  * @param msg Results of search
02279  * @param field Attribute to retrieve
02280  * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
02281  * @return boolean inidicating success
02282 */
02283  BOOL ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
02284                   LDAPMessage *msg, const char *field, SEC_DESC **sd)
02285 {
02286         struct berval **values;
02287         BOOL ret = False;
02288 
02289         values = ldap_get_values_len(ads->ld, msg, field);
02290 
02291         if (!values) return False;
02292 
02293         if (values[0]) {
02294                 prs_struct ps;
02295                 prs_init(&ps, values[0]->bv_len, mem_ctx, UNMARSHALL);
02296                 prs_copy_data_in(&ps, values[0]->bv_val, values[0]->bv_len);
02297                 prs_set_offset(&ps,0);
02298 
02299                 ret = sec_io_desc("sd", sd, &ps, 1);
02300                 prs_mem_free(&ps);
02301         }
02302         
02303         ldap_value_free_len(values);
02304         return ret;
02305 }
02306 
02307 /* 
02308  * in order to support usernames longer than 21 characters we need to 
02309  * use both the sAMAccountName and the userPrincipalName attributes 
02310  * It seems that not all users have the userPrincipalName attribute set
02311  *
02312  * @param ads connection to ads server
02313  * @param mem_ctx TALLOC_CTX for allocating sid array
02314  * @param msg Results of search
02315  * @return the username
02316  */
02317  char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
02318                          LDAPMessage *msg)
02319 {
02320 #if 0   /* JERRY */
02321         char *ret, *p;
02322 
02323         /* lookup_name() only works on the sAMAccountName to 
02324            returning the username portion of userPrincipalName
02325            breaks winbindd_getpwnam() */
02326 
02327         ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
02328         if (ret && (p = strchr_m(ret, '@'))) {
02329                 *p = 0;
02330                 return ret;
02331         }
02332 #endif
02333         return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
02334 }
02335 
02336 
02337 /**
02338  * find the update serial number - this is the core of the ldap cache
02339  * @param ads connection to ads server
02340  * @param ads connection to ADS server
02341  * @param usn Pointer to retrieved update serial number
02342  * @return status of search
02343  **/
02344 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
02345 {
02346         const char *attrs[] = {"highestCommittedUSN", NULL};
02347         ADS_STATUS status;
02348         LDAPMessage *res;
02349 
02350         status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
02351         if (!ADS_ERR_OK(status)) 
02352                 return status;
02353 
02354         if (ads_count_replies(ads, res) != 1) {
02355                 ads_msgfree(ads, res);
02356                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
02357         }
02358 
02359         if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
02360                 ads_msgfree(ads, res);
02361                 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
02362         }
02363 
02364         ads_msgfree(ads, res);
02365         return ADS_SUCCESS;
02366 }
02367 
02368 /* parse a ADS timestring - typical string is
02369    '20020917091222.0Z0' which means 09:12.22 17th September
02370    2002, timezone 0 */
02371 static time_t ads_parse_time(const char *str)
02372 {
02373         struct tm tm;
02374 
02375         ZERO_STRUCT(tm);
02376 
02377         if (sscanf(str, "%4d%2d%2d%2d%2d%2d", 
02378                    &tm.tm_year, &tm.tm_mon, &tm.tm_mday, 
02379                    &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
02380                 return 0;
02381         }
02382         tm.tm_year -= 1900;
02383         tm.tm_mon -= 1;
02384 
02385         return timegm(&tm);
02386 }
02387 
02388 /********************************************************************
02389 ********************************************************************/
02390 
02391 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
02392 {
02393         const char *attrs[] = {"currentTime", NULL};
02394         ADS_STATUS status;
02395         LDAPMessage *res;
02396         char *timestr;
02397         TALLOC_CTX *ctx;
02398         ADS_STRUCT *ads_s = ads;
02399 
02400         if (!(ctx = talloc_init("ads_current_time"))) {
02401                 return ADS_ERROR(LDAP_NO_MEMORY);
02402         }
02403 
02404         /* establish a new ldap tcp session if necessary */
02405 
02406         if ( !ads->ld ) {
02407                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
02408                         ads->server.ldap_server )) == NULL )
02409                 {
02410                         goto done;
02411                 }
02412                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
02413                 status = ads_connect( ads_s );
02414                 if ( !ADS_ERR_OK(status))
02415                         goto done;
02416         }
02417 
02418         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
02419         if (!ADS_ERR_OK(status)) {
02420                 goto done;
02421         }
02422 
02423         timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
02424         if (!timestr) {
02425                 ads_msgfree(ads_s, res);
02426                 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
02427                 goto done;
02428         }
02429 
02430         /* but save the time and offset in the original ADS_STRUCT */   
02431         
02432         ads->config.current_time = ads_parse_time(timestr);
02433 
02434         if (ads->config.current_time != 0) {
02435                 ads->auth.time_offset = ads->config.current_time - time(NULL);
02436                 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
02437         }
02438 
02439         ads_msgfree(ads, res);
02440 
02441         status = ADS_SUCCESS;
02442 
02443 done:
02444         /* free any temporary ads connections */
02445         if ( ads_s != ads ) {
02446                 ads_destroy( &ads_s );
02447         }
02448         talloc_destroy(ctx);
02449 
02450         return status;
02451 }
02452 
02453 /********************************************************************
02454 ********************************************************************/
02455 
02456 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
02457 {
02458         const char *attrs[] = {"domainFunctionality", NULL};
02459         ADS_STATUS status;
02460         LDAPMessage *res;
02461         ADS_STRUCT *ads_s = ads;
02462         
02463         *val = DS_DOMAIN_FUNCTION_2000;
02464 
02465         /* establish a new ldap tcp session if necessary */
02466 
02467         if ( !ads->ld ) {
02468                 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup, 
02469                         ads->server.ldap_server )) == NULL )
02470                 {
02471                         goto done;
02472                 }
02473                 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
02474                 status = ads_connect( ads_s );
02475                 if ( !ADS_ERR_OK(status))
02476                         goto done;
02477         }
02478 
02479         /* If the attribute does not exist assume it is a Windows 2000 
02480            functional domain */
02481            
02482         status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
02483         if (!ADS_ERR_OK(status)) {
02484                 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
02485                         status = ADS_SUCCESS;
02486                 }
02487                 goto done;
02488         }
02489 
02490         if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
02491                 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
02492         }
02493         DEBUG(3,("ads_domain_func_level: %d\n", *val));
02494 
02495         
02496         ads_msgfree(ads, res);
02497 
02498 done:
02499         /* free any temporary ads connections */
02500         if ( ads_s != ads ) {
02501                 ads_destroy( &ads_s );
02502         }
02503 
02504         return status;
02505 }
02506 
02507 /**
02508  * find the domain sid for our domain
02509  * @param ads connection to ads server
02510  * @param sid Pointer to domain sid
02511  * @return status of search
02512  **/
02513 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
02514 {
02515         const char *attrs[] = {"objectSid", NULL};
02516         LDAPMessage *res;
02517         ADS_STATUS rc;
02518 
02519         rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)", 
02520                            attrs, &res);
02521         if (!ADS_ERR_OK(rc)) return rc;
02522         if (!ads_pull_sid(ads, res, "objectSid", sid)) {
02523                 ads_msgfree(ads, res);
02524                 return ADS_ERROR_SYSTEM(ENOENT);
02525         }
02526         ads_msgfree(ads, res);
02527         
02528         return ADS_SUCCESS;
02529 }
02530 
02531 /**
02532  * find our site name 
02533  * @param ads connection to ads server
02534  * @param mem_ctx Pointer to talloc context
02535  * @param site_name Pointer to the sitename
02536  * @return status of search
02537  **/
02538 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
02539 {
02540         ADS_STATUS status;
02541         LDAPMessage *res;
02542         const char *dn, *service_name;
02543         const char *attrs[] = { "dsServiceName", NULL };
02544 
02545         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
02546         if (!ADS_ERR_OK(status)) {
02547                 return status;
02548         }
02549 
02550         service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
02551         if (service_name == NULL) {
02552                 ads_msgfree(ads, res);
02553                 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
02554         }
02555 
02556         ads_msgfree(ads, res);
02557 
02558         /* go up three levels */
02559         dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
02560         if (dn == NULL) {
02561                 return ADS_ERROR(LDAP_NO_MEMORY);
02562         }
02563 
02564         *site_name = talloc_strdup(mem_ctx, dn);
02565         if (*site_name == NULL) {
02566                 return ADS_ERROR(LDAP_NO_MEMORY);
02567         }
02568 
02569         return status;
02570         /*
02571         dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
02572         */                                               
02573 }
02574 
02575 /**
02576  * find the site dn where a machine resides
02577  * @param ads connection to ads server
02578  * @param mem_ctx Pointer to talloc context
02579  * @param computer_name name of the machine
02580  * @param site_name Pointer to the sitename
02581  * @return status of search
02582  **/
02583 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
02584 {
02585         ADS_STATUS status;
02586         LDAPMessage *res;
02587         const char *parent, *config_context, *filter;
02588         const char *attrs[] = { "configurationNamingContext", NULL };
02589         char *dn;
02590 
02591         /* shortcut a query */
02592         if (strequal(computer_name, ads->config.ldap_server_name)) {
02593                 return ads_site_dn(ads, mem_ctx, site_dn);
02594         }
02595 
02596         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
02597         if (!ADS_ERR_OK(status)) {
02598                 return status;
02599         }
02600 
02601         config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
02602         if (config_context == NULL) {
02603                 ads_msgfree(ads, res);
02604                 return ADS_ERROR(LDAP_NO_MEMORY);
02605         }
02606 
02607         filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
02608         if (filter == NULL) {
02609                 ads_msgfree(ads, res);
02610                 return ADS_ERROR(LDAP_NO_MEMORY);
02611         }
02612 
02613         ads_msgfree(ads, res);
02614 
02615         status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE, filter, NULL, &res);
02616         if (!ADS_ERR_OK(status)) {
02617                 return status;
02618         }
02619 
02620         if (ads_count_replies(ads, res) != 1) {
02621                 ads_msgfree(ads, res);
02622                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
02623         }
02624 
02625         dn = ads_get_dn(ads, res);
02626         if (dn == NULL) {
02627                 ads_msgfree(ads, res);
02628                 return ADS_ERROR(LDAP_NO_MEMORY);
02629         }
02630 
02631         /* go up three levels */
02632         parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
02633         if (parent == NULL) {
02634                 ads_msgfree(ads, res);
02635                 ads_memfree(ads, dn);
02636                 return ADS_ERROR(LDAP_NO_MEMORY);
02637         }
02638 
02639         *site_dn = talloc_strdup(mem_ctx, parent);
02640         if (*site_dn == NULL) {
02641                 ads_msgfree(ads, res);
02642                 ads_memfree(ads, dn);
02643                 return ADS_ERROR(LDAP_NO_MEMORY);
02644         }
02645 
02646         ads_memfree(ads, dn);
02647         ads_msgfree(ads, res);
02648 
02649         return status;
02650 }
02651 
02652 /**
02653  * get the upn suffixes for a domain
02654  * @param ads connection to ads server
02655  * @param mem_ctx Pointer to talloc context
02656  * @param suffixes Pointer to an array of suffixes
02657  * @param num_suffixes Pointer to the number of suffixes
02658  * @return status of search
02659  **/
02660 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
02661 {
02662         ADS_STATUS status;
02663         LDAPMessage *res;
02664         const char *config_context, *base;
02665         const char *attrs[] = { "configurationNamingContext", NULL };
02666         const char *attrs2[] = { "uPNSuffixes", NULL };
02667 
02668         status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
02669         if (!ADS_ERR_OK(status)) {
02670                 return status;
02671         }
02672 
02673         config_context = ads_pull_string(ads, mem_ctx, res, "configurationNamingContext");
02674         if (config_context == NULL) {
02675                 ads_msgfree(ads, res);
02676                 return ADS_ERROR(LDAP_NO_MEMORY);
02677         }
02678 
02679         ads_msgfree(ads, res);
02680 
02681         base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
02682         if (base == NULL) {
02683                 return ADS_ERROR(LDAP_NO_MEMORY);
02684         }
02685 
02686         status = ads_search_dn(ads, &res, base, attrs2); 
02687         if (!ADS_ERR_OK(status)) {
02688                 return status;
02689         }
02690 
02691         if (ads_count_replies(ads, res) != 1) {
02692                 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
02693         }
02694 
02695         (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
02696         if ((*suffixes) == NULL) {
02697                 ads_msgfree(ads, res);
02698                 return ADS_ERROR(LDAP_NO_MEMORY);
02699         }
02700 
02701         ads_msgfree(ads, res);
02702 
02703         return status;
02704 }
02705 
02706 /**
02707  * pull a DOM_SID from an extended dn string
02708  * @param mem_ctx TALLOC_CTX 
02709  * @param flags string type of extended_dn
02710  * @param sid pointer to a DOM_SID
02711  * @return boolean inidicating success
02712  **/
02713 BOOL ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx, 
02714                                   const char *dn, 
02715                                   enum ads_extended_dn_flags flags, 
02716                                   DOM_SID *sid)
02717 {
02718         char *p, *q;
02719 
02720         if (!dn) {
02721                 return False;
02722         }
02723 
02724         /* 
02725          * ADS_EXTENDED_DN_HEX_STRING:
02726          * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
02727          *
02728          * ADS_EXTENDED_DN_STRING (only with w2k3):
02729         <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
02730          */
02731 
02732         p = strchr(dn, ';');
02733         if (!p) {
02734                 return False;
02735         }
02736 
02737         if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
02738                 return False;
02739         }
02740 
02741         p += strlen(";<SID=");
02742 
02743         q = strchr(p, '>');
02744         if (!q) {
02745                 return False;
02746         }
02747         
02748         *q = '\0';
02749 
02750         DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
02751 
02752         switch (flags) {
02753         
02754         case ADS_EXTENDED_DN_STRING:
02755                 if (!string_to_sid(sid, p)) {
02756                         return False;
02757                 }
02758                 break;
02759         case ADS_EXTENDED_DN_HEX_STRING: {
02760                 pstring buf;
02761                 size_t buf_len;
02762 
02763                 buf_len = strhex_to_str(buf, strlen(p), p);
02764                 if (buf_len == 0) {
02765                         return False;
02766                 }
02767 
02768                 if (!sid_parse(buf, buf_len, sid)) {
02769                         DEBUG(10,("failed to parse sid\n"));
02770                         return False;
02771                 }
02772                 break;
02773                 }
02774         default:
02775                 DEBUG(10,("unknown extended dn format\n"));
02776                 return False;
02777         }
02778 
02779         return True;
02780 }
02781 
02782 /**
02783  * pull an array of DOM_SIDs from a ADS result
02784  * @param ads connection to ads server
02785  * @param mem_ctx TALLOC_CTX for allocating sid array
02786  * @param msg Results of search
02787  * @param field Attribute to retrieve
02788  * @param flags string type of extended_dn
02789  * @param sids pointer to sid array to allocate
02790  * @return the count of SIDs pulled
02791  **/
02792  int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads, 
02793                                    TALLOC_CTX *mem_ctx, 
02794                                    LDAPMessage *msg, 
02795                                    const char *field,
02796                                    enum ads_extended_dn_flags flags,
02797                                    DOM_SID **sids)
02798 {
02799         int i;
02800         size_t dn_count;
02801         char **dn_strings;
02802 
02803         if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field, 
02804                                            &dn_count)) == NULL) {
02805                 return 0;
02806         }
02807 
02808         (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
02809         if (!(*sids)) {
02810                 TALLOC_FREE(dn_strings);
02811                 return 0;
02812         }
02813 
02814         for (i=0; i<dn_count; i++) {
02815 
02816                 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i], 
02817                                                   flags, &(*sids)[i])) {
02818                         TALLOC_FREE(*sids);
02819                         TALLOC_FREE(dn_strings);
02820                         return 0;
02821                 }
02822         }
02823 
02824         TALLOC_FREE(dn_strings);
02825 
02826         return dn_count;
02827 }
02828 
02829 /********************************************************************
02830 ********************************************************************/
02831 
02832 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
02833 {
02834         LDAPMessage *res = NULL;
02835         ADS_STATUS status;
02836         int count = 0;
02837         char *name = NULL;
02838         
02839         status = ads_find_machine_acct(ads, &res, global_myname());
02840         if (!ADS_ERR_OK(status)) {
02841                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
02842                         global_myname()));
02843                 goto out;
02844         }
02845                 
02846         if ( (count = ads_count_replies(ads, res)) != 1 ) {
02847                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
02848                 goto out;
02849         }
02850                 
02851         if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
02852                 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
02853         }
02854 
02855 out:
02856         ads_msgfree(ads, res);
02857         
02858         return name;
02859 }
02860 
02861 /********************************************************************
02862 ********************************************************************/
02863 
02864 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
02865 {
02866         LDAPMessage *res = NULL;
02867         ADS_STATUS status;
02868         int count = 0;
02869         char *name = NULL;
02870         
02871         status = ads_find_machine_acct(ads, &res, global_myname());
02872         if (!ADS_ERR_OK(status)) {
02873                 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
02874                         global_myname()));
02875                 goto out;
02876         }
02877                 
02878         if ( (count = ads_count_replies(ads, res)) != 1 ) {
02879                 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
02880                 goto out;
02881         }
02882                 
02883         if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
02884                 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
02885         }
02886 
02887 out:
02888         ads_msgfree(ads, res);
02889         
02890         return name;
02891 }
02892 
02893 /********************************************************************
02894 ********************************************************************/
02895 
02896 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
02897 {
02898         LDAPMessage *res = NULL;
02899         ADS_STATUS status;
02900         int count = 0;
02901         char *name = NULL;
02902         
02903         status = ads_find_machine_acct(ads, &res, global_myname());
02904         if (!ADS_ERR_OK(status)) {
02905                 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
02906                         global_myname()));
02907                 goto out;
02908         }
02909                 
02910         if ( (count = ads_count_replies(ads, res)) != 1 ) {
02911                 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
02912                 goto out;
02913         }
02914                 
02915         if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
02916                 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
02917         }
02918 
02919 out:
02920         ads_msgfree(ads, res);
02921         
02922         return name;
02923 }
02924 
02925 #if 0
02926 
02927    SAVED CODE - we used to join via ldap - remember how we did this. JRA.
02928 
02929 /**
02930  * Join a machine to a realm
02931  *  Creates the machine account and sets the machine password
02932  * @param ads connection to ads server
02933  * @param machine name of host to add
02934  * @param org_unit Organizational unit to place machine in
02935  * @return status of join
02936  **/
02937 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
02938                         uint32 account_type, const char *org_unit)
02939 {
02940         ADS_STATUS status;
02941         LDAPMessage *res = NULL;
02942         char *machine;
02943 
02944         /* machine name must be lowercase */
02945         machine = SMB_STRDUP(machine_name);
02946         strlower_m(machine);
02947 
02948         /*
02949         status = ads_find_machine_acct(ads, (void **)&res, machine);
02950         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
02951                 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
02952                 status = ads_leave_realm(ads, machine);
02953                 if (!ADS_ERR_OK(status)) {
02954                         DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
02955                                 machine, ads->config.realm));
02956                         return status;
02957                 }
02958         }
02959         */
02960         status = ads_add_machine_acct(ads, machine, account_type, org_unit);
02961         if (!ADS_ERR_OK(status)) {
02962                 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
02963                 SAFE_FREE(machine);
02964                 return status;
02965         }
02966 
02967         status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
02968         if (!ADS_ERR_OK(status)) {
02969                 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
02970                 SAFE_FREE(machine);
02971                 return status;
02972         }
02973 
02974         SAFE_FREE(machine);
02975         ads_msgfree(ads, res);
02976 
02977         return status;
02978 }
02979 #endif
02980 
02981 /**
02982  * Delete a machine from the realm
02983  * @param ads connection to ads server
02984  * @param hostname Machine to remove
02985  * @return status of delete
02986  **/
02987 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
02988 {
02989         ADS_STATUS status;
02990         void *msg;
02991         LDAPMessage *res;
02992         char *hostnameDN, *host;
02993         int rc;
02994         LDAPControl ldap_control;
02995         LDAPControl  * pldap_control[2] = {NULL, NULL};
02996 
02997         pldap_control[0] = &ldap_control;
02998         memset(&ldap_control, 0, sizeof(LDAPControl));
02999         ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
03000 
03001         /* hostname must be lowercase */
03002         host = SMB_STRDUP(hostname);
03003         strlower_m(host);
03004 
03005         status = ads_find_machine_acct(ads, &res, host);
03006         if (!ADS_ERR_OK(status)) {
03007                 DEBUG(0, ("Host account for %s does not exist.\n", host));
03008                 SAFE_FREE(host);
03009                 return status;
03010         }
03011 
03012         msg = ads_first_entry(ads, res);
03013         if (!msg) {
03014                 SAFE_FREE(host);
03015                 return ADS_ERROR_SYSTEM(ENOENT);
03016         }
03017 
03018         hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
03019 
03020         rc = ldap_delete_ext_s(ads->ld, hostnameDN, pldap_control, NULL);
03021         if (rc) {
03022                 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
03023         }else {
03024                 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
03025         }
03026 
03027         if (rc != LDAP_SUCCESS) {
03028                 const char *attrs[] = { "cn", NULL };
03029                 LDAPMessage *msg_sub;
03030 
03031                 /* we only search with scope ONE, we do not expect any further
03032                  * objects to be created deeper */
03033 
03034                 status = ads_do_search_retry(ads, hostnameDN,
03035                                              LDAP_SCOPE_ONELEVEL,
03036                                              "(objectclass=*)", attrs, &res);
03037 
03038                 if (!ADS_ERR_OK(status)) {
03039                         SAFE_FREE(host);
03040                         ads_memfree(ads, hostnameDN);
03041                         return status;
03042                 }
03043 
03044                 for (msg_sub = ads_first_entry(ads, res); msg_sub;
03045                         msg_sub = ads_next_entry(ads, msg_sub)) {
03046 
03047                         char *dn = NULL;
03048 
03049                         if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
03050                                 SAFE_FREE(host);
03051                                 ads_memfree(ads, hostnameDN);
03052                                 return ADS_ERROR(LDAP_NO_MEMORY);
03053                         }
03054 
03055                         status = ads_del_dn(ads, dn);
03056                         if (!ADS_ERR_OK(status)) {
03057                                 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
03058                                 SAFE_FREE(host);
03059                                 ads_memfree(ads, dn);
03060                                 ads_memfree(ads, hostnameDN);
03061                                 return status;
03062                         }
03063 
03064                         ads_memfree(ads, dn);
03065                 }
03066 
03067                 /* there should be no subordinate objects anymore */
03068                 status = ads_do_search_retry(ads, hostnameDN,
03069                                              LDAP_SCOPE_ONELEVEL,
03070                                              "(objectclass=*)", attrs, &res);
03071 
03072                 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
03073                         SAFE_FREE(host);
03074                         ads_memfree(ads, hostnameDN);
03075                         return status;
03076                 }
03077 
03078                 /* delete hostnameDN now */
03079                 status = ads_del_dn(ads, hostnameDN);
03080                 if (!ADS_ERR_OK(status)) {
03081                         SAFE_FREE(host);
03082                         DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
03083                         ads_memfree(ads, hostnameDN);
03084                         return status;
03085                 }
03086         }
03087 
03088         ads_memfree(ads, hostnameDN);
03089 
03090         status = ads_find_machine_acct(ads, &res, host);
03091         if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
03092                 DEBUG(3, ("Failed to remove host account.\n"));
03093                 SAFE_FREE(host);
03094                 return status;
03095         }
03096 
03097         SAFE_FREE(host);
03098         return status;
03099 }
03100 
03101 #endif

Sambaに対してSat Aug 29 21:23:01 2009に生成されました。  doxygen 1.4.7