BUG 9378: Add extra attributes for AD printer publishing.
[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_find_machine_acct(ads, &res, lp_netbios_name());
196
197         /* We use ldap_get_dn here as we need the answer
198          * in utf8 to call ldap_explode_dn(). JRA. */
199
200         srv_dn_utf8 = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
201         if (!srv_dn_utf8) {
202                 TALLOC_FREE(ctx);
203                 return WERR_SERVER_UNAVAILABLE;
204         }
205         ads_msgfree(ads, res);
206         srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
207         if (!srv_cn_utf8) {
208                 TALLOC_FREE(ctx);
209                 ldap_memfree(srv_dn_utf8);
210                 return WERR_SERVER_UNAVAILABLE;
211         }
212         /* Now convert to CH_UNIX. */
213         if (!pull_utf8_talloc(ctx, &srv_dn, srv_dn_utf8, &converted_size)) {
214                 TALLOC_FREE(ctx);
215                 ldap_memfree(srv_dn_utf8);
216                 ldap_memfree(srv_cn_utf8);
217                 return WERR_SERVER_UNAVAILABLE;
218         }
219         if (!pull_utf8_talloc(ctx, &srv_cn_0, srv_cn_utf8[0], &converted_size)) {
220                 TALLOC_FREE(ctx);
221                 ldap_memfree(srv_dn_utf8);
222                 ldap_memfree(srv_cn_utf8);
223                 TALLOC_FREE(srv_dn);
224                 return WERR_SERVER_UNAVAILABLE;
225         }
226
227         ldap_memfree(srv_dn_utf8);
228         ldap_memfree(srv_cn_utf8);
229
230         srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn_0);
231         if (!srv_cn_escaped) {
232                 TALLOC_FREE(ctx);
233                 return WERR_SERVER_UNAVAILABLE;
234         }
235         sharename_escaped = escape_rdn_val_string_alloc(printer);
236         if (!sharename_escaped) {
237                 SAFE_FREE(srv_cn_escaped);
238                 TALLOC_FREE(ctx);
239                 return WERR_SERVER_UNAVAILABLE;
240         }
241
242         prt_dn = talloc_asprintf(ctx, "cn=%s-%s,%s", srv_cn_escaped, sharename_escaped, srv_dn);
243
244         SAFE_FREE(srv_cn_escaped);
245         SAFE_FREE(sharename_escaped);
246
247         mods = ads_init_mods(ctx);
248
249         if (mods == NULL) {
250                 TALLOC_FREE(ctx);
251                 return WERR_NOMEM;
252         }
253
254         win_rc = nt_printer_info_to_mods(ctx, pinfo2, &mods);
255         if (!W_ERROR_IS_OK(win_rc)) {
256                 TALLOC_FREE(ctx);
257                 return win_rc;
258         }
259
260         /* publish it */
261         ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx, &mods);
262         if (ads_rc.err.rc == LDAP_NO_SUCH_OBJECT) {
263                 int i;
264                 for (i=0; mods[i] != 0; i++)
265                         ;
266                 mods[i] = (LDAPMod *)-1;
267                 ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
268         }
269
270         if (!ADS_ERR_OK(ads_rc)) {
271                 DEBUG(3, ("error publishing %s: %s\n",
272                           printer, ads_errstr(ads_rc)));
273         }
274
275         /* retreive the guid and store it locally */
276         if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
277                 bool guid_ok;
278                 ZERO_STRUCT(guid);
279                 guid_ok = ads_pull_guid(ads, res, &guid);
280                 ads_msgfree(ads, res);
281                 if (guid_ok) {
282                         store_printer_guid(msg_ctx, printer, guid);
283                 }
284         }
285         TALLOC_FREE(ctx);
286
287         return win_rc;
288 }
289
290 static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
291                                        const char *printer)
292 {
293         ADS_STATUS ads_rc;
294         LDAPMessage *res = NULL;
295         char *prt_dn = NULL;
296
297         DEBUG(5, ("unpublishing printer %s\n", printer));
298
299         /* remove the printer from the directory */
300         ads_rc = ads_find_printer_on_server(ads, &res,
301                                             printer, lp_netbios_name());
302
303         if (ADS_ERR_OK(ads_rc) && res && ads_count_replies(ads, res)) {
304                 prt_dn = ads_get_dn(ads, talloc_tos(), res);
305                 if (!prt_dn) {
306                         ads_msgfree(ads, res);
307                         return WERR_NOMEM;
308                 }
309                 ads_rc = ads_del_dn(ads, prt_dn);
310                 TALLOC_FREE(prt_dn);
311         }
312
313         if (res) {
314                 ads_msgfree(ads, res);
315         }
316         return WERR_OK;
317 }
318
319 /****************************************************************************
320  * Publish a printer in the directory
321  *
322  * @param mem_ctx      memory context
323  * @param session_info  session_info to access winreg pipe
324  * @param pinfo2       printer information
325  * @param action       publish/unpublish action
326  * @return WERROR indicating status of publishing
327  ***************************************************************************/
328
329 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
330                           const struct auth_session_info *session_info,
331                           struct messaging_context *msg_ctx,
332                           struct spoolss_PrinterInfo2 *pinfo2,
333                           int action)
334 {
335         uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES;
336         struct spoolss_SetPrinterInfo2 *sinfo2;
337         ADS_STATUS ads_rc;
338         ADS_STRUCT *ads = NULL;
339         WERROR win_rc;
340
341         sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
342         if (!sinfo2) {
343                 return WERR_NOMEM;
344         }
345
346         switch (action) {
347         case DSPRINT_PUBLISH:
348         case DSPRINT_UPDATE:
349                 pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
350                 break;
351         case DSPRINT_UNPUBLISH:
352                 pinfo2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
353                 break;
354         default:
355                 win_rc = WERR_NOT_SUPPORTED;
356                 goto done;
357         }
358
359         sinfo2->attributes = pinfo2->attributes;
360
361         win_rc = winreg_update_printer_internal(mem_ctx, session_info, msg_ctx,
362                                         pinfo2->sharename, info2_mask,
363                                         sinfo2, NULL, NULL);
364         if (!W_ERROR_IS_OK(win_rc)) {
365                 DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
366                 goto done;
367         }
368
369         TALLOC_FREE(sinfo2);
370
371         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
372         if (!ads) {
373                 DEBUG(3, ("ads_init() failed\n"));
374                 win_rc = WERR_SERVER_UNAVAILABLE;
375                 goto done;
376         }
377         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
378         SAFE_FREE(ads->auth.password);
379         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
380                 NULL, NULL);
381
382         /* ads_connect() will find the DC for us */
383         ads_rc = ads_connect(ads);
384         if (!ADS_ERR_OK(ads_rc)) {
385                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
386                 win_rc = WERR_ACCESS_DENIED;
387                 goto done;
388         }
389
390         switch (action) {
391         case DSPRINT_PUBLISH:
392         case DSPRINT_UPDATE:
393                 win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2);
394                 break;
395         case DSPRINT_UNPUBLISH:
396                 win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename);
397                 break;
398         }
399
400 done:
401         ads_destroy(&ads);
402         return win_rc;
403 }
404
405 WERROR check_published_printers(struct messaging_context *msg_ctx)
406 {
407         ADS_STATUS ads_rc;
408         ADS_STRUCT *ads = NULL;
409         int snum;
410         int n_services = lp_numservices();
411         TALLOC_CTX *tmp_ctx = NULL;
412         struct auth_session_info *session_info = NULL;
413         struct spoolss_PrinterInfo2 *pinfo2;
414         NTSTATUS status;
415         WERROR result;
416
417         tmp_ctx = talloc_new(NULL);
418         if (!tmp_ctx) return WERR_NOMEM;
419
420         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
421         if (!ads) {
422                 DEBUG(3, ("ads_init() failed\n"));
423                 return WERR_SERVER_UNAVAILABLE;
424         }
425         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
426         SAFE_FREE(ads->auth.password);
427         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
428                 NULL, NULL);
429
430         /* ads_connect() will find the DC for us */
431         ads_rc = ads_connect(ads);
432         if (!ADS_ERR_OK(ads_rc)) {
433                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
434                 result = WERR_ACCESS_DENIED;
435                 goto done;
436         }
437
438         status = make_session_info_system(tmp_ctx, &session_info);
439         if (!NT_STATUS_IS_OK(status)) {
440                 DEBUG(0, ("check_published_printers: "
441                           "Could not create system session_info\n"));
442                 result = WERR_ACCESS_DENIED;
443                 goto done;
444         }
445
446         for (snum = 0; snum < n_services; snum++) {
447                 if (!lp_snum_ok(snum) || !lp_print_ok(snum)) {
448                         continue;
449                 }
450
451                 result = winreg_get_printer_internal(tmp_ctx, session_info, msg_ctx,
452                                             lp_servicename(talloc_tos(), snum),
453                                             &pinfo2);
454                 if (!W_ERROR_IS_OK(result)) {
455                         continue;
456                 }
457
458                 if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
459                         nt_printer_publish_ads(msg_ctx, ads, pinfo2);
460                 }
461
462                 TALLOC_FREE(pinfo2);
463         }
464
465         result = WERR_OK;
466 done:
467         ads_destroy(&ads);
468         ads_kdestroy("MEMORY:prtpub_cache");
469         talloc_free(tmp_ctx);
470         return result;
471 }
472
473 bool is_printer_published(TALLOC_CTX *mem_ctx,
474                           const struct auth_session_info *session_info,
475                           struct messaging_context *msg_ctx,
476                           const char *servername,
477                           const char *printer,
478                           struct GUID *guid,
479                           struct spoolss_PrinterInfo2 **info2)
480 {
481         struct spoolss_PrinterInfo2 *pinfo2 = NULL;
482         enum winreg_Type type;
483         uint8_t *data;
484         uint32_t data_size;
485         WERROR result;
486         NTSTATUS status;
487         struct dcerpc_binding_handle *b;
488
489         result = winreg_printer_binding_handle(mem_ctx,
490                                                session_info,
491                                                msg_ctx,
492                                                &b);
493         if (!W_ERROR_IS_OK(result)) {
494                 return false;
495         }
496
497         result = winreg_get_printer(mem_ctx, b,
498                                     printer, &pinfo2);
499         if (!W_ERROR_IS_OK(result)) {
500                 return false;
501         }
502
503         if (!(pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED)) {
504                 TALLOC_FREE(pinfo2);
505                 return false;
506         }
507
508         if (!guid) {
509                 goto done;
510         }
511
512         /* fetching printer guids really ought to be a separate function. */
513
514         result = winreg_get_printer_dataex(mem_ctx, b,
515                                            printer,
516                                            SPOOL_DSSPOOLER_KEY, "objectGUID",
517                                            &type, &data, &data_size);
518         if (!W_ERROR_IS_OK(result)) {
519                 TALLOC_FREE(pinfo2);
520                 return false;
521         }
522
523         /* We used to store the guid as REG_BINARY, then swapped
524            to REG_SZ for Vista compatibility so check for both */
525
526         switch (type) {
527         case REG_SZ:
528                 status = GUID_from_string((char *)data, guid);
529                 if (!NT_STATUS_IS_OK(status)) {
530                         TALLOC_FREE(pinfo2);
531                         return false;
532                 }
533                 break;
534
535         case REG_BINARY:
536                 if (data_size != sizeof(struct GUID)) {
537                         TALLOC_FREE(pinfo2);
538                         return false;
539                 }
540                 memcpy(guid, data, sizeof(struct GUID));
541                 break;
542         default:
543                 DEBUG(0,("is_printer_published: GUID value stored as "
544                          "invaluid type (%d)\n", type));
545                 break;
546         }
547
548 done:
549         if (info2) {
550                 *info2 = talloc_move(mem_ctx, &pinfo2);
551         }
552         talloc_free(pinfo2);
553         return true;
554 }
555 #else
556 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
557                           const struct auth_session_info *session_info,
558                           struct messaging_context *msg_ctx,
559                           struct spoolss_PrinterInfo2 *pinfo2,
560                           int action)
561 {
562         return WERR_OK;
563 }
564
565 WERROR check_published_printers(struct messaging_context *msg_ctx)
566 {
567         return WERR_OK;
568 }
569
570 bool is_printer_published(TALLOC_CTX *mem_ctx,
571                           const struct auth_session_info *session_info,
572                           struct messaging_context *msg_ctx,
573                           const char *servername,
574                           const char *printer,
575                           struct GUID *guid,
576                           struct spoolss_PrinterInfo2 **info2)
577 {
578         return False;
579 }
580 #endif /* HAVE_ADS */