r8752: With all the infrustructure done, details like a SamSync migration
[obnox/samba/samba-obnox.git] / source4 / libnet / libnet_samsync_ldb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    
4    Extract the user/system database from a remote SamSync server
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7    Copyright (C) Andrew Tridgell 2004
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24
25 #include "includes.h"
26 #include "libnet/libnet.h"
27 #include "librpc/gen_ndr/ndr_netlogon.h"
28 #include "librpc/gen_ndr/ndr_samr.h"
29 #include "dlinklist.h"
30 #include "lib/ldb/include/ldb.h"
31
32 struct samsync_ldb_secret {
33         struct samsync_ldb_secret *prev, *next;
34         DATA_BLOB secret;
35         char *name;
36         NTTIME mtime;
37 };
38
39 struct samsync_ldb_trusted_domain {
40         struct samsync_ldb_trusted_domain *prev, *next;
41         struct dom_sid *sid;
42         char *name;
43 };
44
45 struct samsync_ldb_state {
46         struct dom_sid *dom_sid[3];
47         struct ldb_context *sam_ldb;
48         char *base_dn[3];
49         struct samsync_ldb_secret *secrets;
50         struct samsync_ldb_trusted_domain *trusted_domains;
51 };
52
53 static NTSTATUS samsync_ldb_handle_domain(TALLOC_CTX *mem_ctx,
54                                           struct samsync_ldb_state *state,
55                                           struct creds_CredentialState *creds,
56                                           enum netr_SamDatabaseID database,
57                                           struct netr_DELTA_ENUM *delta) 
58 {
59         struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
60         const char *domain_name = domain->domain_name.string;
61         struct ldb_message *msg;
62         int ret;
63         
64         if (database == SAM_DATABASE_DOMAIN) {
65                 const char *domain_attrs[] =  {"nETBIOSName", "nCName", NULL};
66                 struct ldb_message **msgs_domain;
67                 int ret_domain;
68                 ret_domain = gendb_search(state->sam_ldb, mem_ctx, NULL, &msgs_domain, domain_attrs,
69                                           "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", 
70                                           domain_name);
71                 if (ret_domain == -1) {
72                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
73                 }
74                 
75                 if (ret_domain != 1) {
76                         return NT_STATUS_NO_SUCH_DOMAIN;                
77                 }
78
79                 state->base_dn[database]
80                         = talloc_steal(state, samdb_result_string(msgs_domain[0], 
81                                                                   "nCName", NULL));
82                 
83                 state->dom_sid[database]
84                         = talloc_steal(state, 
85                                        samdb_search_dom_sid(state->sam_ldb, state,
86                                                             state->base_dn[database], "objectSid", 
87                                                             "dn=%s", state->base_dn[database]));
88         } else if (database == SAM_DATABASE_BUILTIN) {
89                         /* work out the builtin_dn - useful for so many calls its worth
90                            fetching here */
91                 state->base_dn[database]
92                         = talloc_steal(state, 
93                                        samdb_search_string(state->sam_ldb, mem_ctx, NULL,
94                                                            "dn", "objectClass=builtinDomain"));
95                 state->dom_sid[database]
96                         = dom_sid_parse_talloc(state, SID_BUILTIN);
97         } else {
98                 /* PRIVs DB */
99                 return NT_STATUS_INVALID_PARAMETER;
100         }
101
102         msg = ldb_msg_new(mem_ctx);
103         if (msg == NULL) {
104                 return NT_STATUS_NO_MEMORY;
105         }
106
107         msg->dn = talloc_reference(mem_ctx, state->base_dn[database]);
108         if (!msg->dn) {
109                 return NT_STATUS_NO_MEMORY;
110         }
111
112         samdb_msg_add_string(state->sam_ldb, mem_ctx, 
113                              msg, "oEMInformation", domain->comment.string);
114
115         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
116                              msg, "forceLogff", domain->force_logoff_time);
117
118         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
119                           msg, "minPwdLen", domain->min_password_length);
120
121         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
122                           msg, "maxPwdAge", domain->max_password_age);
123
124         samdb_msg_add_int64(state->sam_ldb, mem_ctx, 
125                           msg, "minPwdAge", domain->min_password_age);
126
127         samdb_msg_add_uint(state->sam_ldb, mem_ctx, 
128                           msg, "pwdHistoryLength", domain->password_history_length);
129
130         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
131                              msg, "modifiedCountAtLastProm", 
132                              domain->sequence_num);
133
134         samdb_msg_add_uint64(state->sam_ldb, mem_ctx, 
135                              msg, "creationTime", domain->domain_create_time);
136
137         /* TODO: Account lockout, password properties */
138         
139         ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
140
141         if (ret) {
142                 return NT_STATUS_INTERNAL_ERROR;
143         }
144         return NT_STATUS_OK;
145 }
146
147 static NTSTATUS samsync_ldb_handle_user(TALLOC_CTX *mem_ctx,
148                                         struct samsync_ldb_state *state,
149                                         struct creds_CredentialState *creds,
150                                         enum netr_SamDatabaseID database,
151                                         struct netr_DELTA_ENUM *delta) 
152 {
153         uint32_t rid = delta->delta_id_union.rid;
154         struct netr_DELTA_USER *user = delta->delta_union.user;
155         const char *container, *obj_class;
156         char *cn_name;
157         int cn_name_len;
158
159         struct ldb_message *msg;
160         struct ldb_message **msgs;
161         int ret;
162         uint32_t acb;
163         BOOL add = False;
164         const char *attrs[] = { NULL };
165
166         msg = ldb_msg_new(mem_ctx);
167         if (msg == NULL) {
168                 return NT_STATUS_NO_MEMORY;
169         }
170
171         /* search for the user, by rid */
172         ret = gendb_search(state->sam_ldb, mem_ctx, state->base_dn[database], &msgs, attrs,
173                            "(&(objectClass=user)(objectSid=%s))", 
174                            ldap_encode_ndr_dom_sid(mem_ctx, dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))); 
175
176         if (ret == -1) {
177                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
178         } else if (ret == 0) {
179                 add = True;
180         } else if (ret > 1) {
181                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
182         } else {
183                 msg->dn = talloc_steal(mem_ctx, msgs[0]->dn);
184         }
185
186
187         cn_name   = talloc_strdup(mem_ctx, user->account_name.string);
188         NT_STATUS_HAVE_NO_MEMORY(cn_name);
189         cn_name_len = strlen(cn_name);
190
191 #define ADD_OR_DEL(type, attrib, field) do {\
192         if (user->field) { \
193                 samdb_msg_add_ ## type(state->sam_ldb, mem_ctx, msg, \
194                                      attrib, user->field); \
195         } else if (!add) { \
196                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  \
197                                      attrib); \
198         } \
199         } while (0);
200
201         ADD_OR_DEL(string, "samAccountName", account_name.string);
202         ADD_OR_DEL(string, "displayName", full_name.string);
203
204         if (samdb_msg_add_dom_sid(state->sam_ldb, mem_ctx, msg, 
205                                   "objectSid", dom_sid_add_rid(mem_ctx, state->dom_sid[database], rid))) {
206                 return NT_STATUS_NO_MEMORY; 
207         }
208
209         ADD_OR_DEL(uint, "primaryGroupID", primary_gid);
210         ADD_OR_DEL(string, "homeDirectory", home_directory.string);
211         ADD_OR_DEL(string, "homeDrive", home_drive.string);
212         ADD_OR_DEL(string, "scriptPath", logon_script.string);
213         ADD_OR_DEL(string, "description", description.string);
214         ADD_OR_DEL(string, "userWorkstations", workstations.string);
215
216         ADD_OR_DEL(uint64, "lastLogon", last_logon);
217         ADD_OR_DEL(uint64, "lastLogoff", last_logoff);
218
219         /* TODO: Logon hours */
220
221         ADD_OR_DEL(uint, "badPwdCount", bad_password_count);
222         ADD_OR_DEL(uint, "logonCount", logon_count);
223
224         ADD_OR_DEL(uint64, "pwdLastSet", last_password_change);
225         ADD_OR_DEL(uint64, "accountExpires", acct_expiry);
226         
227         if (samdb_msg_add_acct_flags(state->sam_ldb, mem_ctx, msg, 
228                                      "userAccountConrol", user->acct_flags) != 0) { 
229                 return NT_STATUS_NO_MEMORY; 
230         } 
231         
232         /* Passwords */
233         samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
234                                 "unicodePwd"); 
235         if (user->lm_password_present) {
236                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
237                                    "lmPwdHash", &user->lmpassword);
238         } else {
239                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
240                                      "lmPwdHash"); 
241         }
242         if (user->nt_password_present) {
243                 samdb_msg_add_hash(state->sam_ldb, mem_ctx, msg,  
244                                    "ntPwdHash", &user->ntpassword);
245         } else {
246                 samdb_msg_add_delete(state->sam_ldb, mem_ctx, msg,  
247                                      "ntPwdHash"); 
248         }
249             
250         ADD_OR_DEL(string, "comment", comment.string);
251         ADD_OR_DEL(string, "userParameters", parameters.string);
252         ADD_OR_DEL(uint, "countryCode", country_code);
253         ADD_OR_DEL(uint, "codePage", code_page);
254
255         ADD_OR_DEL(string, "profilePath", profile_path.string);
256
257         acb = user->acct_flags;
258         if (acb & (ACB_WSTRUST)) {
259                 cn_name[cn_name_len - 1] = '\0';
260                 container = "Computers";
261                 obj_class = "computer";
262                 
263         } else if (acb & ACB_SVRTRUST) {
264                 if (cn_name[cn_name_len - 1] != '$') {
265                         return NT_STATUS_FOOBAR;                
266                 }
267                 cn_name[cn_name_len - 1] = '\0';
268                 container = "Domain Controllers";
269                 obj_class = "computer";
270         } else {
271                 container = "Users";
272                 obj_class = "user";
273         }
274         if (add) {
275                 samdb_msg_add_string(state->sam_ldb, mem_ctx, msg, 
276                                      "objectClass", obj_class);
277                 msg->dn = talloc_asprintf(mem_ctx, "CN=%s,CN=%s,%s",
278                                           cn_name, container, state->base_dn[database]);
279                 if (!msg->dn) {
280                         return NT_STATUS_NO_MEMORY;             
281                 }
282
283                 ret = samdb_add(state->sam_ldb, mem_ctx, msg);
284                 if (ret != 0) {
285                         DEBUG(0,("Failed to create user record %s\n", msg->dn));
286                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
287                 }
288         } else {
289                 ret = samdb_replace(state->sam_ldb, mem_ctx, msg);
290                 if (ret != 0) {
291                         DEBUG(0,("Failed to modify user record %s\n", msg->dn));
292                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
293                 }
294         }
295
296         return NT_STATUS_OK;
297 }
298
299 static NTSTATUS samsync_ldb_handle_group(TALLOC_CTX *mem_ctx,
300                                          struct samsync_ldb_state *state,
301                                          struct creds_CredentialState *creds,
302                                          enum netr_SamDatabaseID database,
303                                          struct netr_DELTA_ENUM *delta) 
304 {
305         uint32_t rid = delta->delta_id_union.rid;
306         struct netr_DELTA_GROUP *group = delta->delta_union.group;
307         const char *groupname = group->group_name.string;
308
309         return NT_STATUS_OK;
310 }
311
312 static NTSTATUS libnet_samsync_ldb_fn(TALLOC_CTX *mem_ctx,              
313                                   void *private,                        
314                                   struct creds_CredentialState *creds,
315                                   enum netr_SamDatabaseID database,
316                                   struct netr_DELTA_ENUM *delta,
317                                   char **error_string)
318 {
319         NTSTATUS nt_status = NT_STATUS_OK;
320         struct samsync_ldb_state *state = private;
321
322         *error_string = NULL;
323         switch (delta->delta_type) {
324         case NETR_DELTA_DOMAIN:
325         {
326                 nt_status = samsync_ldb_handle_domain(mem_ctx, 
327                                                       state,
328                                                       creds,
329                                                       database,
330                                                       delta);
331                 break;
332         }
333         case NETR_DELTA_USER:
334         {
335                 nt_status = samsync_ldb_handle_user(mem_ctx, 
336                                                     state,
337                                                     creds,
338                                                     database,
339                                                     delta);
340                 break;
341         }
342         case NETR_DELTA_GROUP:
343         {
344                 nt_status = samsync_ldb_handle_group(mem_ctx, 
345                                                      state,
346                                                      creds,
347                                                      database,
348                                                      delta);
349                 break;
350         }
351         default:
352                 /* Can't dump them all right now */
353                 break;
354         }
355         return nt_status;
356 }
357
358 static NTSTATUS libnet_samsync_ldb_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
359 {
360         NTSTATUS nt_status;
361         struct libnet_SamSync r2;
362         struct samsync_ldb_state *state = talloc(mem_ctx, struct samsync_ldb_state);
363
364         if (!state) {
365                 return NT_STATUS_NO_MEMORY;
366         }
367
368         state->secrets = NULL;
369         state->trusted_domains = NULL;
370
371         state->sam_ldb = samdb_connect(state);
372
373         
374
375         r2.error_string = NULL;
376         r2.delta_fn = libnet_samsync_ldb_fn;
377         r2.fn_ctx = state;
378         r2.machine_account = NULL; /* TODO:  Create a machine account, fill this in, and the delete it */
379         nt_status = libnet_SamSync_netlogon(ctx, state, &r2);
380         r->error_string = r2.error_string;
381
382         if (!NT_STATUS_IS_OK(nt_status)) {
383                 talloc_free(state);
384                 return nt_status;
385         }
386         talloc_free(state);
387         return nt_status;
388 }
389
390
391
392 static NTSTATUS libnet_samsync_ldb_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
393 {
394         NTSTATUS nt_status;
395         struct libnet_samsync_ldb r2;
396         r2.level = LIBNET_SAMSYNC_LDB_NETLOGON;
397         r2.error_string = NULL;
398         nt_status = libnet_samsync_ldb(ctx, mem_ctx, &r2);
399         r->error_string = r2.error_string;
400         
401         return nt_status;
402 }
403
404 NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_samsync_ldb *r)
405 {
406         switch (r->level) {
407         case LIBNET_SAMSYNC_LDB_GENERIC:
408                 return libnet_samsync_ldb_generic(ctx, mem_ctx, r);
409         case LIBNET_SAMSYNC_LDB_NETLOGON:
410                 return libnet_samsync_ldb_netlogon(ctx, mem_ctx, r);
411         }
412
413         return NT_STATUS_INVALID_LEVEL;
414 }