net_vampire: add code to vampire a SAM database to a keytab file.
[samba.git] / source3 / libnet / libnet_samsync_keytab.c
1 /*
2    Unix SMB/CIFS implementation.
3    dump the remote SAM using rpc samsync operations
4
5    Copyright (C) Guenther Deschner 2008.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "utils/net.h"
23
24 #if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
25
26 /****************************************************************
27 ****************************************************************/
28
29 struct samsync_keytab_entry {
30         const char *name;
31         const char *principal;
32         DATA_BLOB password;
33         uint32_t kvno;
34 };
35
36 struct samsync_keytab_context {
37         krb5_context context;
38         krb5_keytab keytab;
39         const char *keytab_name;
40         ADS_STRUCT *ads;
41         const char *dns_domain_name;
42         uint8_t zero_buf[16];
43         uint32_t count;
44         struct samsync_keytab_entry *entries;
45 };
46
47 /****************************************************************
48 ****************************************************************/
49
50 static int keytab_close(struct samsync_keytab_context *ctx)
51 {
52         if (!ctx) {
53                 return 0;
54         }
55
56         if (ctx->keytab && ctx->context) {
57                 krb5_kt_close(ctx->context, ctx->keytab);
58         }
59
60         if (ctx->context) {
61                 krb5_free_context(ctx->context);
62         }
63
64         if (ctx->ads) {
65                 ads_destroy(&ctx->ads);
66         }
67
68         TALLOC_FREE(ctx);
69
70         return 0;
71 }
72
73 /****************************************************************
74 ****************************************************************/
75
76 static krb5_error_code keytab_init(TALLOC_CTX *mem_ctx,
77                                    const char *keytab_name,
78                                    struct samsync_keytab_context **ctx)
79 {
80         krb5_error_code ret = 0;
81         krb5_context context = NULL;
82         krb5_keytab keytab = NULL;
83         const char *keytab_string = NULL;
84
85         struct samsync_keytab_context *r;
86
87         r = TALLOC_ZERO_P(mem_ctx, struct samsync_keytab_context);
88         if (!r) {
89                 return ENOMEM;
90         }
91
92         talloc_set_destructor(r, keytab_close);
93
94         initialize_krb5_error_table();
95         ret = krb5_init_context(&context);
96         if (ret) {
97                 DEBUG(1,("keytab_init: could not krb5_init_context: %s\n",
98                         error_message(ret)));
99                 return ret;
100         }
101
102         ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab);
103         if (ret) {
104                 DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
105                         error_message(ret)));
106                 krb5_free_context(context);
107                 return ret;
108         }
109
110         ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string);
111         if (ret) {
112                 krb5_kt_close(context, keytab);
113                 krb5_free_context(context);
114                 return ret;
115         }
116
117         r->context = context;
118         r->keytab = keytab;
119         r->keytab_name = keytab_string;
120
121         *ctx = r;
122
123         return 0;
124 }
125
126 /****************************************************************
127 ****************************************************************/
128
129 static NTSTATUS keytab_ad_connect(TALLOC_CTX *mem_ctx,
130                                   const char *domain_name,
131                                   const char *username,
132                                   const char *password,
133                                   struct samsync_keytab_context *ctx)
134 {
135         NTSTATUS status;
136         ADS_STATUS ad_status;
137         ADS_STRUCT *ads;
138         struct netr_DsRGetDCNameInfo *info = NULL;
139         const char *dc;
140
141         status = dsgetdcname(mem_ctx, NULL, domain_name, NULL, NULL, 0, &info);
142         if (!NT_STATUS_IS_OK(status)) {
143                 return status;
144         }
145
146         dc = strip_hostname(info->dc_unc);
147
148         ads = ads_init(NULL, domain_name, dc);
149         NT_STATUS_HAVE_NO_MEMORY(ads);
150
151         if (getenv(KRB5_ENV_CCNAME) == NULL) {
152                 setenv(KRB5_ENV_CCNAME, "MEMORY:libnet_samsync_keytab", 1);
153         }
154
155         ads->auth.user_name = SMB_STRDUP(username);
156         ads->auth.password = SMB_STRDUP(password);
157
158         ad_status = ads_connect_user_creds(ads);
159         if (!ADS_ERR_OK(ad_status)) {
160                 return NT_STATUS_UNSUCCESSFUL;
161         }
162
163         ctx->ads = ads;
164
165         ctx->dns_domain_name = talloc_strdup_upper(mem_ctx, ads->config.realm);
166         NT_STATUS_HAVE_NO_MEMORY(ctx->dns_domain_name);
167
168         return NT_STATUS_OK;
169 }
170
171 /****************************************************************
172 ****************************************************************/
173
174 static krb5_error_code keytab_add(struct samsync_keytab_context *ctx)
175 {
176         krb5_error_code ret = 0;
177         krb5_enctype enctypes[2] = { ENCTYPE_ARCFOUR_HMAC, 0 };
178         int i;
179
180         for (i=0; i<ctx->count; i++) {
181
182                 struct samsync_keytab_entry *entry = &ctx->entries[i];
183                 krb5_data password;
184                 krb5_kvno kvno;
185
186                 kvno = ads_get_kvno(ctx->ads, entry->name);
187
188                 password.data = (char *)entry->password.data;
189                 password.length = entry->password.length;
190
191                 ret = smb_krb5_kt_add_entry(ctx->context,
192                                             ctx->keytab,
193                                             kvno,
194                                             entry->principal,
195                                             enctypes,
196                                             password,
197                                             true);
198                 if (ret) {
199                         DEBUG(1,("keytab_add: Failed to add entry to keytab file\n"));
200                         return ret;
201                 }
202         }
203
204         return ret;
205 }
206
207 /****************************************************************
208 ****************************************************************/
209
210 static NTSTATUS fetch_sam_entry_keytab(TALLOC_CTX *mem_ctx,
211                                        enum netr_SamDatabaseID database_id,
212                                        uint32_t rid,
213                                        struct netr_DELTA_USER *r,
214                                        NTSTATUS status,
215                                        struct samsync_keytab_context *ctx)
216 {
217         uchar nt_passwd[16];
218         struct samsync_keytab_entry *entry;
219
220         if (memcmp(r->ntpassword.hash, ctx->zero_buf, 16) == 0) {
221                 return NT_STATUS_OK;
222         }
223
224         entry = TALLOC_ZERO_P(mem_ctx, struct samsync_keytab_entry);
225         NT_STATUS_HAVE_NO_MEMORY(entry);
226
227         sam_pwd_hash(rid, r->ntpassword.hash, nt_passwd, 0);
228
229         entry->name = talloc_strdup(mem_ctx, r->account_name.string);
230         entry->principal = talloc_asprintf(mem_ctx, "%s@%s",
231                                            r->account_name.string,
232                                            ctx->dns_domain_name);
233         entry->password = data_blob_talloc(mem_ctx, nt_passwd, 16);
234
235         NT_STATUS_HAVE_NO_MEMORY(entry->name);
236         NT_STATUS_HAVE_NO_MEMORY(entry->principal);
237
238         ADD_TO_ARRAY(mem_ctx, struct samsync_keytab_entry, *entry,
239                      &ctx->entries, &ctx->count);
240
241         return NT_STATUS_OK;
242 }
243
244 /****************************************************************
245 ****************************************************************/
246
247 NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
248                                   enum netr_SamDatabaseID database_id,
249                                   struct netr_DELTA_ENUM_ARRAY *r,
250                                   NTSTATUS result,
251                                   struct samsync_context *ctx)
252 {
253         NTSTATUS status = NT_STATUS_OK;
254         krb5_error_code ret = 0;
255         struct samsync_keytab_context *keytab_ctx = NULL;
256         int i;
257
258         ret = keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
259         if (ret) {
260                 status = krb5_to_nt_status(ret);
261                 goto out;
262         }
263
264         status = keytab_ad_connect(mem_ctx,
265                                    ctx->domain_name,
266                                    ctx->username,
267                                    ctx->password,
268                                    keytab_ctx);
269         if (!NT_STATUS_IS_OK(status)) {
270                 goto out;
271         }
272
273         for (i = 0; i < r->num_deltas; i++) {
274
275                 if (r->delta_enum[i].delta_type != NETR_DELTA_USER) {
276                         continue;
277                 }
278
279                 status = fetch_sam_entry_keytab(mem_ctx, database_id,
280                                                 r->delta_enum[i].delta_id_union.rid,
281                                                 r->delta_enum[i].delta_union.user,
282                                                 result,
283                                                 keytab_ctx);
284                 if (!NT_STATUS_IS_OK(status)) {
285                         goto out;
286                 }
287         }
288
289         ret = keytab_add(keytab_ctx);
290         if (ret) {
291                 status = krb5_to_nt_status(ret);
292                 ctx->error_message = talloc_asprintf(mem_ctx,
293                         "Failed to add entries to keytab %s: %s",
294                         keytab_ctx->keytab_name, error_message(ret));
295                 goto out;
296         }
297
298         ctx->result_message = talloc_asprintf(mem_ctx,
299                 "vampired %d accounts to keytab %s",
300                 keytab_ctx->count,
301                 keytab_ctx->keytab_name);
302  out:
303         TALLOC_FREE(keytab_ctx);
304
305         return status;
306 }
307
308 #else
309
310 NTSTATUS fetch_sam_entries_keytab(TALLOC_CTX *mem_ctx,
311                                   enum netr_SamDatabaseID database_id,
312                                   struct netr_DELTA_ENUM_ARRAY *r,
313                                   NTSTATUS result,
314                                   struct samsync_context *ctx)
315 {
316         return NT_STATUS_NOT_SUPPORTED;
317 }
318
319 #endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */