56e68439ed2fde0ff634bd2d200eaaa28e0d25aa
[samba.git] / source3 / printing / nt_printing_ads.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  RPC Pipe client / server routines
4  *  Copyright (C) Andrew Tridgell              1992-2000,
5  *  Copyright (C) Jean François Micouleau      1998-2000.
6  *  Copyright (C) Gerald Carter                2002-2005.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23 #include "../librpc/gen_ndr/spoolss.h"
24 #include "rpc_server/spoolss/srv_spoolss_util.h"
25 #include "nt_printing.h"
26 #include "ads.h"
27 #include "secrets.h"
28 #include "krb5_env.h"
29 #include "../libcli/registry/util_reg.h"
30
31 #ifdef HAVE_ADS
32 /*****************************************************************
33  ****************************************************************/
34
35 static void store_printer_guid(struct messaging_context *msg_ctx,
36                                const char *printer, struct GUID guid)
37 {
38         TALLOC_CTX *tmp_ctx;
39         struct auth_serversupplied_info *session_info = NULL;
40         const char *guid_str;
41         DATA_BLOB blob;
42         NTSTATUS status;
43         WERROR result;
44
45         tmp_ctx = talloc_new(NULL);
46         if (!tmp_ctx) {
47                 DEBUG(0, ("store_printer_guid: Out of memory?!\n"));
48                 return;
49         }
50
51         status = make_session_info_system(tmp_ctx, &session_info);
52         if (!NT_STATUS_IS_OK(status)) {
53                 DEBUG(0, ("store_printer_guid: "
54                           "Could not create system session_info\n"));
55                 goto done;
56         }
57
58         guid_str = GUID_string(tmp_ctx, &guid);
59         if (!guid_str) {
60                 DEBUG(0, ("store_printer_guid: Out of memory?!\n"));
61                 goto done;
62         }
63
64         /* We used to store this as a REG_BINARY but that causes
65            Vista to whine */
66
67         if (!push_reg_sz(tmp_ctx, &blob, guid_str)) {
68                 DEBUG(0, ("store_printer_guid: "
69                           "Could not marshall string %s for objectGUID\n",
70                           guid_str));
71                 goto done;
72         }
73
74         result = winreg_set_printer_dataex(tmp_ctx, session_info, msg_ctx,
75                                            printer,
76                                            SPOOL_DSSPOOLER_KEY, "objectGUID",
77                                            REG_SZ, blob.data, blob.length);
78         if (!W_ERROR_IS_OK(result)) {
79                 DEBUG(0, ("store_printer_guid: "
80                           "Failed to store GUID for printer %s\n", printer));
81         }
82
83 done:
84         talloc_free(tmp_ctx);
85 }
86
87 static WERROR nt_printer_publish_ads(struct messaging_context *msg_ctx,
88                                      ADS_STRUCT *ads,
89                                      struct spoolss_PrinterInfo2 *pinfo2)
90 {
91         ADS_STATUS ads_rc;
92         LDAPMessage *res;
93         char *prt_dn = NULL, *srv_dn, *srv_cn_0, *srv_cn_escaped, *sharename_escaped;
94         char *srv_dn_utf8, **srv_cn_utf8;
95         TALLOC_CTX *ctx;
96         ADS_MODLIST mods;
97         const char *attrs[] = {"objectGUID", NULL};
98         struct GUID guid;
99         WERROR win_rc = WERR_OK;
100         size_t converted_size;
101         const char *printer = pinfo2->sharename;
102
103         /* build the ads mods */
104         ctx = talloc_init("nt_printer_publish_ads");
105         if (ctx == NULL) {
106                 return WERR_NOMEM;
107         }
108
109         DEBUG(5, ("publishing printer %s\n", printer));
110
111         /* figure out where to publish */
112         ads_find_machine_acct(ads, &res, global_myname());
113
114         /* We use ldap_get_dn here as we need the answer
115          * in utf8 to call ldap_explode_dn(). JRA. */
116
117         srv_dn_utf8 = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
118         if (!srv_dn_utf8) {
119                 TALLOC_FREE(ctx);
120                 return WERR_SERVER_UNAVAILABLE;
121         }
122         ads_msgfree(ads, res);
123         srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
124         if (!srv_cn_utf8) {
125                 TALLOC_FREE(ctx);
126                 ldap_memfree(srv_dn_utf8);
127                 return WERR_SERVER_UNAVAILABLE;
128         }
129         /* Now convert to CH_UNIX. */
130         if (!pull_utf8_talloc(ctx, &srv_dn, srv_dn_utf8, &converted_size)) {
131                 TALLOC_FREE(ctx);
132                 ldap_memfree(srv_dn_utf8);
133                 ldap_memfree(srv_cn_utf8);
134                 return WERR_SERVER_UNAVAILABLE;
135         }
136         if (!pull_utf8_talloc(ctx, &srv_cn_0, srv_cn_utf8[0], &converted_size)) {
137                 TALLOC_FREE(ctx);
138                 ldap_memfree(srv_dn_utf8);
139                 ldap_memfree(srv_cn_utf8);
140                 TALLOC_FREE(srv_dn);
141                 return WERR_SERVER_UNAVAILABLE;
142         }
143
144         ldap_memfree(srv_dn_utf8);
145         ldap_memfree(srv_cn_utf8);
146
147         srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn_0);
148         if (!srv_cn_escaped) {
149                 TALLOC_FREE(ctx);
150                 return WERR_SERVER_UNAVAILABLE;
151         }
152         sharename_escaped = escape_rdn_val_string_alloc(printer);
153         if (!sharename_escaped) {
154                 SAFE_FREE(srv_cn_escaped);
155                 TALLOC_FREE(ctx);
156                 return WERR_SERVER_UNAVAILABLE;
157         }
158
159         prt_dn = talloc_asprintf(ctx, "cn=%s-%s,%s", srv_cn_escaped, sharename_escaped, srv_dn);
160
161         SAFE_FREE(srv_cn_escaped);
162         SAFE_FREE(sharename_escaped);
163
164         mods = ads_init_mods(ctx);
165
166         if (mods == NULL) {
167                 SAFE_FREE(prt_dn);
168                 TALLOC_FREE(ctx);
169                 return WERR_NOMEM;
170         }
171
172         ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME, printer);
173
174         /* publish it */
175         ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx, &mods);
176         if (ads_rc.err.rc == LDAP_NO_SUCH_OBJECT) {
177                 int i;
178                 for (i=0; mods[i] != 0; i++)
179                         ;
180                 mods[i] = (LDAPMod *)-1;
181                 ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
182         }
183
184         if (!ADS_ERR_OK(ads_rc)) {
185                 DEBUG(3, ("error publishing %s: %s\n",
186                           printer, ads_errstr(ads_rc)));
187         }
188
189         /* retreive the guid and store it locally */
190         if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
191                 bool guid_ok;
192                 ZERO_STRUCT(guid);
193                 guid_ok = ads_pull_guid(ads, res, &guid);
194                 ads_msgfree(ads, res);
195                 if (guid_ok) {
196                         store_printer_guid(msg_ctx, printer, guid);
197                 }
198         }
199         TALLOC_FREE(ctx);
200
201         return win_rc;
202 }
203
204 static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
205                                        const char *printer)
206 {
207         ADS_STATUS ads_rc;
208         LDAPMessage *res = NULL;
209         char *prt_dn = NULL;
210
211         DEBUG(5, ("unpublishing printer %s\n", printer));
212
213         /* remove the printer from the directory */
214         ads_rc = ads_find_printer_on_server(ads, &res,
215                                             printer, global_myname());
216
217         if (ADS_ERR_OK(ads_rc) && res && ads_count_replies(ads, res)) {
218                 prt_dn = ads_get_dn(ads, talloc_tos(), res);
219                 if (!prt_dn) {
220                         ads_msgfree(ads, res);
221                         return WERR_NOMEM;
222                 }
223                 ads_rc = ads_del_dn(ads, prt_dn);
224                 TALLOC_FREE(prt_dn);
225         }
226
227         if (res) {
228                 ads_msgfree(ads, res);
229         }
230         return WERR_OK;
231 }
232
233 /****************************************************************************
234  * Publish a printer in the directory
235  *
236  * @param mem_ctx      memory context
237  * @param session_info  session_info to access winreg pipe
238  * @param pinfo2       printer information
239  * @param action       publish/unpublish action
240  * @return WERROR indicating status of publishing
241  ***************************************************************************/
242
243 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
244                           const struct auth_serversupplied_info *session_info,
245                           struct messaging_context *msg_ctx,
246                           struct spoolss_PrinterInfo2 *pinfo2,
247                           int action)
248 {
249         uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES;
250         struct spoolss_SetPrinterInfo2 *sinfo2;
251         ADS_STATUS ads_rc;
252         ADS_STRUCT *ads = NULL;
253         WERROR win_rc;
254
255         sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
256         if (!sinfo2) {
257                 return WERR_NOMEM;
258         }
259
260         switch (action) {
261         case DSPRINT_PUBLISH:
262         case DSPRINT_UPDATE:
263                 pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
264                 break;
265         case DSPRINT_UNPUBLISH:
266                 pinfo2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
267                 break;
268         default:
269                 win_rc = WERR_NOT_SUPPORTED;
270                 goto done;
271         }
272
273         sinfo2->attributes = pinfo2->attributes;
274
275         win_rc = winreg_update_printer(mem_ctx, session_info, msg_ctx,
276                                         pinfo2->sharename, info2_mask,
277                                         sinfo2, NULL, NULL);
278         if (!W_ERROR_IS_OK(win_rc)) {
279                 DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
280                 goto done;
281         }
282
283         TALLOC_FREE(sinfo2);
284
285         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
286         if (!ads) {
287                 DEBUG(3, ("ads_init() failed\n"));
288                 win_rc = WERR_SERVER_UNAVAILABLE;
289                 goto done;
290         }
291         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
292         SAFE_FREE(ads->auth.password);
293         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
294                 NULL, NULL);
295
296         /* ads_connect() will find the DC for us */
297         ads_rc = ads_connect(ads);
298         if (!ADS_ERR_OK(ads_rc)) {
299                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
300                 win_rc = WERR_ACCESS_DENIED;
301                 goto done;
302         }
303
304         switch (action) {
305         case DSPRINT_PUBLISH:
306         case DSPRINT_UPDATE:
307                 win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2);
308                 break;
309         case DSPRINT_UNPUBLISH:
310                 win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename);
311                 break;
312         }
313
314 done:
315         ads_destroy(&ads);
316         return win_rc;
317 }
318
319 WERROR check_published_printers(struct messaging_context *msg_ctx)
320 {
321         ADS_STATUS ads_rc;
322         ADS_STRUCT *ads = NULL;
323         int snum;
324         int n_services = lp_numservices();
325         TALLOC_CTX *tmp_ctx = NULL;
326         struct auth_serversupplied_info *session_info = NULL;
327         struct spoolss_PrinterInfo2 *pinfo2;
328         NTSTATUS status;
329         WERROR result;
330
331         tmp_ctx = talloc_new(NULL);
332         if (!tmp_ctx) return WERR_NOMEM;
333
334         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
335         if (!ads) {
336                 DEBUG(3, ("ads_init() failed\n"));
337                 return WERR_SERVER_UNAVAILABLE;
338         }
339         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
340         SAFE_FREE(ads->auth.password);
341         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
342                 NULL, NULL);
343
344         /* ads_connect() will find the DC for us */
345         ads_rc = ads_connect(ads);
346         if (!ADS_ERR_OK(ads_rc)) {
347                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
348                 result = WERR_ACCESS_DENIED;
349                 goto done;
350         }
351
352         status = make_session_info_system(tmp_ctx, &session_info);
353         if (!NT_STATUS_IS_OK(status)) {
354                 DEBUG(0, ("check_published_printers: "
355                           "Could not create system session_info\n"));
356                 result = WERR_ACCESS_DENIED;
357                 goto done;
358         }
359
360         for (snum = 0; snum < n_services; snum++) {
361                 if (!lp_snum_ok(snum) || !lp_print_ok(snum)) {
362                         continue;
363                 }
364
365                 result = winreg_get_printer(tmp_ctx, session_info, msg_ctx,
366                                             lp_servicename(snum),
367                                             &pinfo2);
368                 if (!W_ERROR_IS_OK(result)) {
369                         continue;
370                 }
371
372                 if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
373                         nt_printer_publish_ads(msg_ctx, ads, pinfo2);
374                 }
375
376                 TALLOC_FREE(pinfo2);
377         }
378
379         result = WERR_OK;
380 done:
381         ads_destroy(&ads);
382         ads_kdestroy("MEMORY:prtpub_cache");
383         talloc_free(tmp_ctx);
384         return result;
385 }
386
387 bool is_printer_published(TALLOC_CTX *mem_ctx,
388                           const struct auth_serversupplied_info *session_info,
389                           struct messaging_context *msg_ctx,
390                           const char *servername, char *printer, struct GUID *guid,
391                           struct spoolss_PrinterInfo2 **info2)
392 {
393         struct spoolss_PrinterInfo2 *pinfo2 = NULL;
394         enum winreg_Type type;
395         uint8_t *data;
396         uint32_t data_size;
397         WERROR result;
398         NTSTATUS status;
399
400         result = winreg_get_printer(mem_ctx, session_info, msg_ctx,
401                                     printer, &pinfo2);
402         if (!W_ERROR_IS_OK(result)) {
403                 return false;
404         }
405
406         if (!(pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED)) {
407                 TALLOC_FREE(pinfo2);
408                 return false;
409         }
410
411         if (!guid) {
412                 goto done;
413         }
414
415         /* fetching printer guids really ought to be a separate function. */
416
417         result = winreg_get_printer_dataex(mem_ctx, session_info, msg_ctx,
418                                            printer,
419                                            SPOOL_DSSPOOLER_KEY, "objectGUID",
420                                            &type, &data, &data_size);
421         if (!W_ERROR_IS_OK(result)) {
422                 TALLOC_FREE(pinfo2);
423                 return false;
424         }
425
426         /* We used to store the guid as REG_BINARY, then swapped
427            to REG_SZ for Vista compatibility so check for both */
428
429         switch (type) {
430         case REG_SZ:
431                 status = GUID_from_string((char *)data, guid);
432                 if (!NT_STATUS_IS_OK(status)) {
433                         TALLOC_FREE(pinfo2);
434                         return false;
435                 }
436                 break;
437
438         case REG_BINARY:
439                 if (data_size != sizeof(struct GUID)) {
440                         TALLOC_FREE(pinfo2);
441                         return false;
442                 }
443                 memcpy(guid, data, sizeof(struct GUID));
444                 break;
445         default:
446                 DEBUG(0,("is_printer_published: GUID value stored as "
447                          "invaluid type (%d)\n", type));
448                 break;
449         }
450
451 done:
452         if (info2) {
453                 *info2 = talloc_move(mem_ctx, &pinfo2);
454         }
455         talloc_free(pinfo2);
456         return true;
457 }
458 #else
459 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
460                           const struct auth_serversupplied_info *session_info,
461                           struct messaging_context *msg_ctx,
462                           struct spoolss_PrinterInfo2 *pinfo2,
463                           int action)
464 {
465         return WERR_OK;
466 }
467
468 WERROR check_published_printers(struct messaging_context *msg_ctx)
469 {
470         return WERR_OK;
471 }
472
473 bool is_printer_published(TALLOC_CTX *mem_ctx,
474                           const struct auth_serversupplied_info *session_info,
475                           struct messaging_context *msg_ctx,
476                           const char *servername, char *printer, struct GUID *guid,
477                           struct spoolss_PrinterInfo2 **info2)
478 {
479         return False;
480 }
481 #endif /* HAVE_ADS */