s3: piddir creation fix part 2.
[ira/wip.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_publish_ads(struct messaging_context *msg_ctx,
91                                      ADS_STRUCT *ads,
92                                      struct spoolss_PrinterInfo2 *pinfo2)
93 {
94         ADS_STATUS ads_rc;
95         LDAPMessage *res;
96         char *prt_dn = NULL, *srv_dn, *srv_cn_0, *srv_cn_escaped, *sharename_escaped;
97         char *srv_dn_utf8, **srv_cn_utf8;
98         TALLOC_CTX *ctx;
99         ADS_MODLIST mods;
100         const char *attrs[] = {"objectGUID", NULL};
101         struct GUID guid;
102         WERROR win_rc = WERR_OK;
103         size_t converted_size;
104         const char *printer = pinfo2->sharename;
105
106         /* build the ads mods */
107         ctx = talloc_init("nt_printer_publish_ads");
108         if (ctx == NULL) {
109                 return WERR_NOMEM;
110         }
111
112         DEBUG(5, ("publishing printer %s\n", printer));
113
114         /* figure out where to publish */
115         ads_find_machine_acct(ads, &res, lp_netbios_name());
116
117         /* We use ldap_get_dn here as we need the answer
118          * in utf8 to call ldap_explode_dn(). JRA. */
119
120         srv_dn_utf8 = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
121         if (!srv_dn_utf8) {
122                 TALLOC_FREE(ctx);
123                 return WERR_SERVER_UNAVAILABLE;
124         }
125         ads_msgfree(ads, res);
126         srv_cn_utf8 = ldap_explode_dn(srv_dn_utf8, 1);
127         if (!srv_cn_utf8) {
128                 TALLOC_FREE(ctx);
129                 ldap_memfree(srv_dn_utf8);
130                 return WERR_SERVER_UNAVAILABLE;
131         }
132         /* Now convert to CH_UNIX. */
133         if (!pull_utf8_talloc(ctx, &srv_dn, srv_dn_utf8, &converted_size)) {
134                 TALLOC_FREE(ctx);
135                 ldap_memfree(srv_dn_utf8);
136                 ldap_memfree(srv_cn_utf8);
137                 return WERR_SERVER_UNAVAILABLE;
138         }
139         if (!pull_utf8_talloc(ctx, &srv_cn_0, srv_cn_utf8[0], &converted_size)) {
140                 TALLOC_FREE(ctx);
141                 ldap_memfree(srv_dn_utf8);
142                 ldap_memfree(srv_cn_utf8);
143                 TALLOC_FREE(srv_dn);
144                 return WERR_SERVER_UNAVAILABLE;
145         }
146
147         ldap_memfree(srv_dn_utf8);
148         ldap_memfree(srv_cn_utf8);
149
150         srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn_0);
151         if (!srv_cn_escaped) {
152                 TALLOC_FREE(ctx);
153                 return WERR_SERVER_UNAVAILABLE;
154         }
155         sharename_escaped = escape_rdn_val_string_alloc(printer);
156         if (!sharename_escaped) {
157                 SAFE_FREE(srv_cn_escaped);
158                 TALLOC_FREE(ctx);
159                 return WERR_SERVER_UNAVAILABLE;
160         }
161
162         prt_dn = talloc_asprintf(ctx, "cn=%s-%s,%s", srv_cn_escaped, sharename_escaped, srv_dn);
163
164         SAFE_FREE(srv_cn_escaped);
165         SAFE_FREE(sharename_escaped);
166
167         mods = ads_init_mods(ctx);
168
169         if (mods == NULL) {
170                 SAFE_FREE(prt_dn);
171                 TALLOC_FREE(ctx);
172                 return WERR_NOMEM;
173         }
174
175         ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME, printer);
176
177         /* publish it */
178         ads_rc = ads_mod_printer_entry(ads, prt_dn, ctx, &mods);
179         if (ads_rc.err.rc == LDAP_NO_SUCH_OBJECT) {
180                 int i;
181                 for (i=0; mods[i] != 0; i++)
182                         ;
183                 mods[i] = (LDAPMod *)-1;
184                 ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
185         }
186
187         if (!ADS_ERR_OK(ads_rc)) {
188                 DEBUG(3, ("error publishing %s: %s\n",
189                           printer, ads_errstr(ads_rc)));
190         }
191
192         /* retreive the guid and store it locally */
193         if (ADS_ERR_OK(ads_search_dn(ads, &res, prt_dn, attrs))) {
194                 bool guid_ok;
195                 ZERO_STRUCT(guid);
196                 guid_ok = ads_pull_guid(ads, res, &guid);
197                 ads_msgfree(ads, res);
198                 if (guid_ok) {
199                         store_printer_guid(msg_ctx, printer, guid);
200                 }
201         }
202         TALLOC_FREE(ctx);
203
204         return win_rc;
205 }
206
207 static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
208                                        const char *printer)
209 {
210         ADS_STATUS ads_rc;
211         LDAPMessage *res = NULL;
212         char *prt_dn = NULL;
213
214         DEBUG(5, ("unpublishing printer %s\n", printer));
215
216         /* remove the printer from the directory */
217         ads_rc = ads_find_printer_on_server(ads, &res,
218                                             printer, lp_netbios_name());
219
220         if (ADS_ERR_OK(ads_rc) && res && ads_count_replies(ads, res)) {
221                 prt_dn = ads_get_dn(ads, talloc_tos(), res);
222                 if (!prt_dn) {
223                         ads_msgfree(ads, res);
224                         return WERR_NOMEM;
225                 }
226                 ads_rc = ads_del_dn(ads, prt_dn);
227                 TALLOC_FREE(prt_dn);
228         }
229
230         if (res) {
231                 ads_msgfree(ads, res);
232         }
233         return WERR_OK;
234 }
235
236 /****************************************************************************
237  * Publish a printer in the directory
238  *
239  * @param mem_ctx      memory context
240  * @param session_info  session_info to access winreg pipe
241  * @param pinfo2       printer information
242  * @param action       publish/unpublish action
243  * @return WERROR indicating status of publishing
244  ***************************************************************************/
245
246 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
247                           const struct auth_session_info *session_info,
248                           struct messaging_context *msg_ctx,
249                           struct spoolss_PrinterInfo2 *pinfo2,
250                           int action)
251 {
252         uint32_t info2_mask = SPOOLSS_PRINTER_INFO_ATTRIBUTES;
253         struct spoolss_SetPrinterInfo2 *sinfo2;
254         ADS_STATUS ads_rc;
255         ADS_STRUCT *ads = NULL;
256         WERROR win_rc;
257
258         sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
259         if (!sinfo2) {
260                 return WERR_NOMEM;
261         }
262
263         switch (action) {
264         case DSPRINT_PUBLISH:
265         case DSPRINT_UPDATE:
266                 pinfo2->attributes |= PRINTER_ATTRIBUTE_PUBLISHED;
267                 break;
268         case DSPRINT_UNPUBLISH:
269                 pinfo2->attributes ^= PRINTER_ATTRIBUTE_PUBLISHED;
270                 break;
271         default:
272                 win_rc = WERR_NOT_SUPPORTED;
273                 goto done;
274         }
275
276         sinfo2->attributes = pinfo2->attributes;
277
278         win_rc = winreg_update_printer_internal(mem_ctx, session_info, msg_ctx,
279                                         pinfo2->sharename, info2_mask,
280                                         sinfo2, NULL, NULL);
281         if (!W_ERROR_IS_OK(win_rc)) {
282                 DEBUG(3, ("err %d saving data\n", W_ERROR_V(win_rc)));
283                 goto done;
284         }
285
286         TALLOC_FREE(sinfo2);
287
288         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
289         if (!ads) {
290                 DEBUG(3, ("ads_init() failed\n"));
291                 win_rc = WERR_SERVER_UNAVAILABLE;
292                 goto done;
293         }
294         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
295         SAFE_FREE(ads->auth.password);
296         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
297                 NULL, NULL);
298
299         /* ads_connect() will find the DC for us */
300         ads_rc = ads_connect(ads);
301         if (!ADS_ERR_OK(ads_rc)) {
302                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
303                 win_rc = WERR_ACCESS_DENIED;
304                 goto done;
305         }
306
307         switch (action) {
308         case DSPRINT_PUBLISH:
309         case DSPRINT_UPDATE:
310                 win_rc = nt_printer_publish_ads(msg_ctx, ads, pinfo2);
311                 break;
312         case DSPRINT_UNPUBLISH:
313                 win_rc = nt_printer_unpublish_ads(ads, pinfo2->sharename);
314                 break;
315         }
316
317 done:
318         ads_destroy(&ads);
319         return win_rc;
320 }
321
322 WERROR check_published_printers(struct messaging_context *msg_ctx)
323 {
324         ADS_STATUS ads_rc;
325         ADS_STRUCT *ads = NULL;
326         int snum;
327         int n_services = lp_numservices();
328         TALLOC_CTX *tmp_ctx = NULL;
329         struct auth_session_info *session_info = NULL;
330         struct spoolss_PrinterInfo2 *pinfo2;
331         NTSTATUS status;
332         WERROR result;
333
334         tmp_ctx = talloc_new(NULL);
335         if (!tmp_ctx) return WERR_NOMEM;
336
337         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
338         if (!ads) {
339                 DEBUG(3, ("ads_init() failed\n"));
340                 return WERR_SERVER_UNAVAILABLE;
341         }
342         setenv(KRB5_ENV_CCNAME, "MEMORY:prtpub_cache", 1);
343         SAFE_FREE(ads->auth.password);
344         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(),
345                 NULL, NULL);
346
347         /* ads_connect() will find the DC for us */
348         ads_rc = ads_connect(ads);
349         if (!ADS_ERR_OK(ads_rc)) {
350                 DEBUG(3, ("ads_connect failed: %s\n", ads_errstr(ads_rc)));
351                 result = WERR_ACCESS_DENIED;
352                 goto done;
353         }
354
355         status = make_session_info_system(tmp_ctx, &session_info);
356         if (!NT_STATUS_IS_OK(status)) {
357                 DEBUG(0, ("check_published_printers: "
358                           "Could not create system session_info\n"));
359                 result = WERR_ACCESS_DENIED;
360                 goto done;
361         }
362
363         for (snum = 0; snum < n_services; snum++) {
364                 if (!lp_snum_ok(snum) || !lp_print_ok(snum)) {
365                         continue;
366                 }
367
368                 result = winreg_get_printer_internal(tmp_ctx, session_info, msg_ctx,
369                                             lp_servicename(snum),
370                                             &pinfo2);
371                 if (!W_ERROR_IS_OK(result)) {
372                         continue;
373                 }
374
375                 if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
376                         nt_printer_publish_ads(msg_ctx, ads, pinfo2);
377                 }
378
379                 TALLOC_FREE(pinfo2);
380         }
381
382         result = WERR_OK;
383 done:
384         ads_destroy(&ads);
385         ads_kdestroy("MEMORY:prtpub_cache");
386         talloc_free(tmp_ctx);
387         return result;
388 }
389
390 bool is_printer_published(TALLOC_CTX *mem_ctx,
391                           const struct auth_session_info *session_info,
392                           struct messaging_context *msg_ctx,
393                           const char *servername, char *printer, struct GUID *guid,
394                           struct spoolss_PrinterInfo2 **info2)
395 {
396         struct spoolss_PrinterInfo2 *pinfo2 = NULL;
397         enum winreg_Type type;
398         uint8_t *data;
399         uint32_t data_size;
400         WERROR result;
401         NTSTATUS status;
402         struct dcerpc_binding_handle *b;
403
404         result = winreg_printer_binding_handle(mem_ctx,
405                                                session_info,
406                                                msg_ctx,
407                                                &b);
408         if (!W_ERROR_IS_OK(result)) {
409                 return false;
410         }
411
412         result = winreg_get_printer(mem_ctx, b,
413                                     printer, &pinfo2);
414         if (!W_ERROR_IS_OK(result)) {
415                 return false;
416         }
417
418         if (!(pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED)) {
419                 TALLOC_FREE(pinfo2);
420                 return false;
421         }
422
423         if (!guid) {
424                 goto done;
425         }
426
427         /* fetching printer guids really ought to be a separate function. */
428
429         result = winreg_get_printer_dataex(mem_ctx, b,
430                                            printer,
431                                            SPOOL_DSSPOOLER_KEY, "objectGUID",
432                                            &type, &data, &data_size);
433         if (!W_ERROR_IS_OK(result)) {
434                 TALLOC_FREE(pinfo2);
435                 return false;
436         }
437
438         /* We used to store the guid as REG_BINARY, then swapped
439            to REG_SZ for Vista compatibility so check for both */
440
441         switch (type) {
442         case REG_SZ:
443                 status = GUID_from_string((char *)data, guid);
444                 if (!NT_STATUS_IS_OK(status)) {
445                         TALLOC_FREE(pinfo2);
446                         return false;
447                 }
448                 break;
449
450         case REG_BINARY:
451                 if (data_size != sizeof(struct GUID)) {
452                         TALLOC_FREE(pinfo2);
453                         return false;
454                 }
455                 memcpy(guid, data, sizeof(struct GUID));
456                 break;
457         default:
458                 DEBUG(0,("is_printer_published: GUID value stored as "
459                          "invaluid type (%d)\n", type));
460                 break;
461         }
462
463 done:
464         if (info2) {
465                 *info2 = talloc_move(mem_ctx, &pinfo2);
466         }
467         talloc_free(pinfo2);
468         return true;
469 }
470 #else
471 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
472                           const struct auth_session_info *session_info,
473                           struct messaging_context *msg_ctx,
474                           struct spoolss_PrinterInfo2 *pinfo2,
475                           int action)
476 {
477         return WERR_OK;
478 }
479
480 WERROR check_published_printers(struct messaging_context *msg_ctx)
481 {
482         return WERR_OK;
483 }
484
485 bool is_printer_published(TALLOC_CTX *mem_ctx,
486                           const struct auth_session_info *session_info,
487                           struct messaging_context *msg_ctx,
488                           const char *servername, char *printer, struct GUID *guid,
489                           struct spoolss_PrinterInfo2 **info2)
490 {
491         return False;
492 }
493 #endif /* HAVE_ADS */