3a2baf40f5e742c6e446b0e6ab6b764a07fb4a11
[obnox/samba/samba-obnox.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 #include "auth.h"
31 #include "../librpc/ndr/libndr.h"
32 #include "rpc_client/cli_winreg_spoolss.h"
33
34 #ifdef HAVE_ADS
35 /*****************************************************************
36  ****************************************************************/
37
38 static void store_printer_guid(struct messaging_context *msg_ctx,
39                                const char *printer, struct GUID guid)
40 {
41         TALLOC_CTX *tmp_ctx;
42         struct auth_session_info *session_info = NULL;
43         const char *guid_str;
44         DATA_BLOB blob;
45         NTSTATUS status;
46         WERROR result;
47
48         tmp_ctx = talloc_new(NULL);
49         if (!tmp_ctx) {
50                 DEBUG(0, ("store_printer_guid: Out of memory?!\n"));
51                 return;
52         }
53
54         status = make_session_info_system(tmp_ctx, &session_info);
55         if (!NT_STATUS_IS_OK(status)) {
56                 DEBUG(0, ("store_printer_guid: "
57                           "Could not create system session_info\n"));
58                 goto done;
59         }
60
61         guid_str = GUID_string(tmp_ctx, &guid);
62         if (!guid_str) {
63                 DEBUG(0, ("store_printer_guid: Out of memory?!\n"));
64                 goto done;
65         }
66
67         /* We used to store this as a REG_BINARY but that causes
68            Vista to whine */
69
70         if (!push_reg_sz(tmp_ctx, &blob, guid_str)) {
71                 DEBUG(0, ("store_printer_guid: "
72                           "Could not marshall string %s for objectGUID\n",
73                           guid_str));
74                 goto done;
75         }
76
77         result = winreg_set_printer_dataex_internal(tmp_ctx, session_info, msg_ctx,
78                                            printer,
79                                            SPOOL_DSSPOOLER_KEY, "objectGUID",
80                                            REG_SZ, blob.data, blob.length);
81         if (!W_ERROR_IS_OK(result)) {
82                 DEBUG(0, ("store_printer_guid: "
83                           "Failed to store GUID for printer %s\n", printer));
84         }
85
86 done:
87         talloc_free(tmp_ctx);
88 }
89
90 static WERROR nt_printer_info_to_mods(TALLOC_CTX *ctx,
91                                       struct spoolss_PrinterInfo2 *info2,
92                                       ADS_MODLIST *mods)
93 {
94         char *info_str;
95
96         ads_mod_str(ctx, mods, SPOOL_REG_PRINTERNAME, info2->sharename);
97         ads_mod_str(ctx, mods, SPOOL_REG_SHORTSERVERNAME, lp_netbios_name());
98         ads_mod_str(ctx, mods, SPOOL_REG_SERVERNAME, get_mydnsfullname());
99
100         info_str = talloc_asprintf(ctx, "\\\\%s\\%s",
101                                    get_mydnsfullname(), info2->sharename);
102         if (info_str == NULL) {
103                 return WERR_NOMEM;
104         }
105         ads_mod_str(ctx, mods, SPOOL_REG_UNCNAME, info_str);
106
107         info_str = talloc_asprintf(ctx, "%d", 4);
108         if (info_str == NULL) {
109                 return WERR_NOMEM;
110         }
111         ads_mod_str(ctx, mods, SPOOL_REG_VERSIONNUMBER, info_str);
112
113         /* empty strings in the mods list result in an attrubute error */
114         if (strlen(info2->drivername) != 0)
115                 ads_mod_str(ctx, mods, SPOOL_REG_DRIVERNAME, info2->drivername);
116         if (strlen(info2->location) != 0)
117                 ads_mod_str(ctx, mods, SPOOL_REG_LOCATION, info2->location);
118         if (strlen(info2->comment) != 0)
119                 ads_mod_str(ctx, mods, SPOOL_REG_DESCRIPTION, info2->comment);
120         if (strlen(info2->portname) != 0)
121                 ads_mod_str(ctx, mods, SPOOL_REG_PORTNAME, info2->portname);
122         if (strlen(info2->sepfile) != 0)
123                 ads_mod_str(ctx, mods, SPOOL_REG_PRINTSEPARATORFILE, info2->sepfile);
124
125         info_str = talloc_asprintf(ctx, "%u", info2->starttime);
126         if (info_str == NULL) {
127                 return WERR_NOMEM;
128         }
129         ads_mod_str(ctx, mods, SPOOL_REG_PRINTSTARTTIME, info_str);
130
131         info_str = talloc_asprintf(ctx, "%u", info2->untiltime);
132         if (info_str == NULL) {
133                 return WERR_NOMEM;
134         }
135         ads_mod_str(ctx, mods, SPOOL_REG_PRINTENDTIME, info_str);
136
137         info_str = talloc_asprintf(ctx, "%u", info2->priority);
138         if (info_str == NULL) {
139                 return WERR_NOMEM;
140         }
141         ads_mod_str(ctx, mods, SPOOL_REG_PRIORITY, info_str);
142
143         if (info2->attributes & PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS) {
144                 ads_mod_str(ctx, mods, SPOOL_REG_PRINTKEEPPRINTEDJOBS, "TRUE");
145         } else {
146                 ads_mod_str(ctx, mods, SPOOL_REG_PRINTKEEPPRINTEDJOBS, "FALSE");
147         }
148
149         switch (info2->attributes & 0x3) {
150         case 0:
151                 ads_mod_str(ctx, mods, SPOOL_REG_PRINTSPOOLING,
152                             SPOOL_REGVAL_PRINTWHILESPOOLING);
153                 break;
154         case 1:
155                 ads_mod_str(ctx, mods, SPOOL_REG_PRINTSPOOLING,
156                             SPOOL_REGVAL_PRINTAFTERSPOOLED);
157                 break;
158         case 2:
159                 ads_mod_str(ctx, mods, SPOOL_REG_PRINTSPOOLING,
160                             SPOOL_REGVAL_PRINTDIRECT);
161                 break;
162         default:
163                 DEBUG(3, ("unsupported printer attributes %x\n",
164                           info2->attributes));
165         }
166
167         return WERR_OK;
168 }
169
170 static WERROR nt_printer_publish_ads(struct messaging_context *msg_ctx,
171                                      ADS_STRUCT *ads,
172                                      struct spoolss_PrinterInfo2 *pinfo2)
173 {
174         ADS_STATUS ads_rc;
175         LDAPMessage *res;
176         char *prt_dn = NULL, *srv_dn, *srv_cn_0, *srv_cn_escaped, *sharename_escaped;
177         char *srv_dn_utf8, **srv_cn_utf8;
178         TALLOC_CTX *ctx;
179         ADS_MODLIST mods;
180         const char *attrs[] = {"objectGUID", NULL};
181         struct GUID guid;
182         WERROR win_rc = WERR_OK;
183         size_t converted_size;
184         const char *printer = pinfo2->sharename;
185
186         /* build the ads mods */
187         ctx = talloc_init("nt_printer_publish_ads");
188         if (ctx == NULL) {
189                 return WERR_NOMEM;
190         }
191
192         DEBUG(5, ("publishing printer %s\n", printer));
193
194         /* figure out where to publish */
195         ads_rc = ads_find_machine_acct(ads, &res, lp_netbios_name());
196         if (!ADS_ERR_OK(ads_rc)) {
197                 DEBUG(0, ("failed to find machine account for %s\n",
198                           lp_netbios_name()));
199                 TALLOC_FREE(ctx);
200                 return WERR_NOT_FOUND;
201         }
202
203         /* We use ldap_get_dn here as we need the answer
204          * in utf8 to call ldap_explode_dn(). JRA. */
205
206         srv_dn_utf8 = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
207         ads_msgfree(ads, res);
208         if (!srv_dn_utf8) {
209                 TALLOC_FREE(ctx);
210                 return WERR_SERVER_UNAVAILABLE;
211         }
212         srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
213         if (!srv_cn_utf8) {
214                 TALLOC_FREE(ctx);
215                 ldap_memfree(srv_dn_utf8);
216                 return WERR_SERVER_UNAVAILABLE;
217         }
218         /* Now convert to CH_UNIX. */
219         if (!pull_utf8_talloc(ctx, &srv_dn, srv_dn_utf8, &converted_size)) {
220                 TALLOC_FREE(ctx);
221                 ldap_memfree(srv_dn_utf8);
222                 ldap_memfree(srv_cn_utf8);
223                 return WERR_SERVER_UNAVAILABLE;
224         }
225         if (!pull_utf8_talloc(ctx, &srv_cn_0, srv_cn_utf8[0], &converted_size)) {
226                 TALLOC_FREE(ctx);
227                 ldap_memfree(srv_dn_utf8);
228                 ldap_memfree(srv_cn_utf8);
229                 TALLOC_FREE(srv_dn);
230                 return WERR_SERVER_UNAVAILABLE;
231         }
232
233         ldap_memfree(srv_dn_utf8);
234         ldap_memfree(srv_cn_utf8);
235
236         srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn_0);
237         if (!srv_cn_escaped) {
238                 TALLOC_FREE(ctx);
239                 return WERR_SERVER_UNAVAILABLE;
240         }
241         sharename_escaped = escape_rdn_val_string_alloc(printer);
242         if (!sharename_escaped) {
243                 SAFE_FREE(srv_cn_escaped);
244                 TALLOC_FREE(ctx);
245                 return WERR_SERVER_UNAVAILABLE;
246         }
247
248         prt_dn = talloc_asprintf(ctx, "cn=%s-%s,%s", srv_cn_escaped, sharename_escaped, srv_dn);
249
250         SAFE_FREE(srv_cn_escaped);
251         SAFE_FREE(sharename_escaped);
252
253         mods = ads_init_mods(ctx);
254
255         if (mods == NULL) {
256                 TALLOC_FREE(ctx);
257                 return WERR_NOMEM;
258         }
259
260         win_rc = nt_printer_info_to_mods(ctx, pinfo2, &mods);
261         if (!W_ERROR_IS_OK(win_rc)) {
262                 TALLOC_FREE(ctx);
263                 return win_rc;
264         }
265
266         /* publish it */
267         ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx, &mods);
268         if (ads_rc.err.rc == LDAP_NO_SUCH_OBJECT) {
269                 int i;
270                 for (i=0; mods[i] != 0; i++)
271                         ;
272                 mods[i] = (LDAPMod *)-1;
273                 ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
274         }
275
276         if (!ADS_ERR_OK(ads_rc)) {
277                 DEBUG(3, ("error publishing %s: %s\n",
278                           printer, ads_errstr(ads_rc)));
279         }
280
281         /* retreive the guid and store it locally */
282         if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
283                 bool guid_ok;
284                 ZERO_STRUCT(guid);
285                 guid_ok = ads_pull_guid(ads, res, &guid);
286                 ads_msgfree(ads, res);
287                 if (guid_ok) {
288                         store_printer_guid(msg_ctx, printer, guid);
289                 }
290         }
291         TALLOC_FREE(ctx);
292
293         return win_rc;
294 }
295
296 static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
297                                        const char *printer)
298 {
299         ADS_STATUS ads_rc;
300         LDAPMessage *res = NULL;
301         char *prt_dn = NULL;
302
303         DEBUG(5, ("unpublishing printer %s\n", printer));
304
305         /* remove the printer from the directory */
306         ads_rc = ads_find_printer_on_server(ads, &res,
307                                             printer, lp_netbios_name());
308
309         if (ADS_ERR_OK(ads_rc) && res && ads_count_replies(ads, res)) {
310                 prt_dn = ads_get_dn(ads, talloc_tos(), res);
311                 if (!prt_dn) {
312                         ads_msgfree(ads, res);
313                         return WERR_NOMEM;
314                 }
315                 ads_rc = ads_del_dn(ads, prt_dn);
316                 TALLOC_FREE(prt_dn);
317         }
318
319         if (res) {
320                 ads_msgfree(ads, res);
321         }
322         return WERR_OK;
323 }
324
325 /****************************************************************************
326  * Publish a printer in the directory
327  *
328  * @param mem_ctx      memory context
329  * @param session_info  session_info to access winreg pipe
330  * @param pinfo2       printer information
331  * @param action       publish/unpublish action
332  * @return WERROR indicating status of publishing
333  ***************************************************************************/
334
335 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
336                           const struct auth_session_info *session_info,
337                           struct messaging_context *msg_ctx,
338                           struct spoolss_PrinterInfo2 *pinfo2,
339                           int action)
340 {
341         uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES;
342         struct spoolss_SetPrinterInfo2 *sinfo2;
343         ADS_STATUS ads_rc;
344         ADS_STRUCT *ads = NULL;
345         WERROR win_rc;
346
347         sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
348         if (!sinfo2) {
349                 return WERR_NOMEM;
350         }
351
352         switch (action) {
353         case DSPRINT_PUBLISH:
354         case DSPRINT_UPDATE:
355                 pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
356                 break;
357         case DSPRINT_UNPUBLISH:
358                 pinfo2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
359                 break;
360         default:
361                 win_rc = WERR_NOT_SUPPORTED;
362                 goto done;
363         }
364
365         sinfo2->attributes = pinfo2->attributes;
366
367         win_rc = winreg_update_printer_internal(mem_ctx, session_info, msg_ctx,
368                                         pinfo2->sharename, info2_mask,
369                                         sinfo2, NULL, NULL);
370         if (!W_ERROR_IS_OK(win_rc)) {
371                 DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
372                 goto done;
373         }
374
375         TALLOC_FREE(sinfo2);
376
377         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
378         if (!ads) {
379                 DEBUG(3, ("ads_init() failed\n"));
380                 win_rc = WERR_SERVER_UNAVAILABLE;
381                 goto done;
382         }
383         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
384         SAFE_FREE(ads->auth.password);
385         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
386                 NULL, NULL);
387
388         /* ads_connect() will find the DC for us */
389         ads_rc = ads_connect(ads);
390         if (!ADS_ERR_OK(ads_rc)) {
391                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
392                 win_rc = WERR_ACCESS_DENIED;
393                 goto done;
394         }
395
396         switch (action) {
397         case DSPRINT_PUBLISH:
398         case DSPRINT_UPDATE:
399                 win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2);
400                 break;
401         case DSPRINT_UNPUBLISH:
402                 win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename);
403                 break;
404         }
405
406 done:
407         ads_destroy(&ads);
408         return win_rc;
409 }
410
411 WERROR check_published_printers(struct messaging_context *msg_ctx)
412 {
413         ADS_STATUS ads_rc;
414         ADS_STRUCT *ads = NULL;
415         int snum;
416         int n_services = lp_numservices();
417         TALLOC_CTX *tmp_ctx = NULL;
418         struct auth_session_info *session_info = NULL;
419         struct spoolss_PrinterInfo2 *pinfo2;
420         NTSTATUS status;
421         WERROR result;
422
423         tmp_ctx = talloc_new(NULL);
424         if (!tmp_ctx) return WERR_NOMEM;
425
426         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
427         if (!ads) {
428                 DEBUG(3, ("ads_init() failed\n"));
429                 return WERR_SERVER_UNAVAILABLE;
430         }
431         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
432         SAFE_FREE(ads->auth.password);
433         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
434                 NULL, NULL);
435
436         /* ads_connect() will find the DC for us */
437         ads_rc = ads_connect(ads);
438         if (!ADS_ERR_OK(ads_rc)) {
439                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
440                 result = WERR_ACCESS_DENIED;
441                 goto done;
442         }
443
444         status = make_session_info_system(tmp_ctx, &session_info);
445         if (!NT_STATUS_IS_OK(status)) {
446                 DEBUG(0, ("check_published_printers: "
447                           "Could not create system session_info\n"));
448                 result = WERR_ACCESS_DENIED;
449                 goto done;
450         }
451
452         for (snum = 0; snum < n_services; snum++) {
453                 if (!lp_snum_ok(snum) || !lp_print_ok(snum)) {
454                         continue;
455                 }
456
457                 result = winreg_get_printer_internal(tmp_ctx, session_info, msg_ctx,
458                                             lp_servicename(talloc_tos(), snum),
459                                             &pinfo2);
460                 if (!W_ERROR_IS_OK(result)) {
461                         continue;
462                 }
463
464                 if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
465                         nt_printer_publish_ads(msg_ctx, ads, pinfo2);
466                 }
467
468                 TALLOC_FREE(pinfo2);
469         }
470
471         result = WERR_OK;
472 done:
473         ads_destroy(&ads);
474         ads_kdestroy("MEMORY:prtpub_cache");
475         talloc_free(tmp_ctx);
476         return result;
477 }
478
479 bool is_printer_published(TALLOC_CTX *mem_ctx,
480                           const struct auth_session_info *session_info,
481                           struct messaging_context *msg_ctx,
482                           const char *servername,
483                           const char *printer,
484                           struct GUID *guid,
485                           struct spoolss_PrinterInfo2 **info2)
486 {
487         struct spoolss_PrinterInfo2 *pinfo2 = NULL;
488         enum winreg_Type type;
489         uint8_t *data;
490         uint32_t data_size;
491         WERROR result;
492         NTSTATUS status;
493         struct dcerpc_binding_handle *b;
494
495         result = winreg_printer_binding_handle(mem_ctx,
496                                                session_info,
497                                                msg_ctx,
498                                                &b);
499         if (!W_ERROR_IS_OK(result)) {
500                 return false;
501         }
502
503         result = winreg_get_printer(mem_ctx, b,
504                                     printer, &pinfo2);
505         if (!W_ERROR_IS_OK(result)) {
506                 return false;
507         }
508
509         if (!(pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED)) {
510                 TALLOC_FREE(pinfo2);
511                 return false;
512         }
513
514         if (!guid) {
515                 goto done;
516         }
517
518         /* fetching printer guids really ought to be a separate function. */
519
520         result = winreg_get_printer_dataex(mem_ctx, b,
521                                            printer,
522                                            SPOOL_DSSPOOLER_KEY, "objectGUID",
523                                            &type, &data, &data_size);
524         if (!W_ERROR_IS_OK(result)) {
525                 TALLOC_FREE(pinfo2);
526                 return false;
527         }
528
529         /* We used to store the guid as REG_BINARY, then swapped
530            to REG_SZ for Vista compatibility so check for both */
531
532         switch (type) {
533         case REG_SZ:
534                 status = GUID_from_string((char *)data, guid);
535                 if (!NT_STATUS_IS_OK(status)) {
536                         TALLOC_FREE(pinfo2);
537                         return false;
538                 }
539                 break;
540
541         case REG_BINARY:
542                 if (data_size != sizeof(struct GUID)) {
543                         TALLOC_FREE(pinfo2);
544                         return false;
545                 }
546                 memcpy(guid, data, sizeof(struct GUID));
547                 break;
548         default:
549                 DEBUG(0,("is_printer_published: GUID value stored as "
550                          "invaluid type (%d)\n", type));
551                 break;
552         }
553
554 done:
555         if (info2) {
556                 *info2 = talloc_move(mem_ctx, &pinfo2);
557         }
558         talloc_free(pinfo2);
559         return true;
560 }
561 #else
562 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
563                           const struct auth_session_info *session_info,
564                           struct messaging_context *msg_ctx,
565                           struct spoolss_PrinterInfo2 *pinfo2,
566                           int action)
567 {
568         return WERR_OK;
569 }
570
571 WERROR check_published_printers(struct messaging_context *msg_ctx)
572 {
573         return WERR_OK;
574 }
575
576 bool is_printer_published(TALLOC_CTX *mem_ctx,
577                           const struct auth_session_info *session_info,
578                           struct messaging_context *msg_ctx,
579                           const char *servername,
580                           const char *printer,
581                           struct GUID *guid,
582                           struct spoolss_PrinterInfo2 **info2)
583 {
584         return False;
585 }
586 #endif /* HAVE_ADS */