s3: Fix a winbind race leading to 100% CPU
[Samba.git] / source3 / printing / nt_printing_ads.c
blob67046bc6fc521a902a8eaa15b2eb28aeefe2f0d2
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.
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.
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.
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/>.
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"
34 #ifdef HAVE_ADS
35 /*****************************************************************
36 ****************************************************************/
38 static void store_printer_guid(struct messaging_context *msg_ctx,
39 const char *printer, struct GUID guid)
41 TALLOC_CTX *tmp_ctx;
42 struct auth_serversupplied_info *session_info = NULL;
43 const char *guid_str;
44 DATA_BLOB blob;
45 NTSTATUS status;
46 WERROR result;
48 tmp_ctx = talloc_new(NULL);
49 if (!tmp_ctx) {
50 DEBUG(0, ("store_printer_guid: Out of memory?!\n"));
51 return;
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;
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;
67 /* We used to store this as a REG_BINARY but that causes
68 Vista to whine */
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;
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));
86 done:
87 talloc_free(tmp_ctx);
90 static WERROR nt_printer_publish_ads(struct messaging_context *msg_ctx,
91 ADS_STRUCT *ads,
92 struct spoolss_PrinterInfo2 *pinfo2)
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;
106 /* build the ads mods */
107 ctx = talloc_init("nt_printer_publish_ads");
108 if (ctx == NULL) {
109 return WERR_NOMEM;
112 DEBUG(5, ("publishing printer %s\n", printer));
114 /* figure out where to publish */
115 ads_find_machine_acct(ads, &res, global_myname());
117 /* We use ldap_get_dn here as we need the answer
118 * in utf8 to call ldap_explode_dn(). JRA. */
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;
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;
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;
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;
147 ldap_memfree(srv_dn_utf8);
148 ldap_memfree(srv_cn_utf8);
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;
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;
162 prt_dn = talloc_asprintf(ctx, "cn=%s-%s,%s", srv_cn_escaped, sharename_escaped, srv_dn);
164 SAFE_FREE(srv_cn_escaped);
165 SAFE_FREE(sharename_escaped);
167 mods = ads_init_mods(ctx);
169 if (mods == NULL) {
170 SAFE_FREE(prt_dn);
171 TALLOC_FREE(ctx);
172 return WERR_NOMEM;
175 ads_mod_str(ctx, &mods, SPOOL_REG_PRINTERNAME, printer);
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++)
183 mods[i] = (LDAPMod *)-1;
184 ads_rc = ads_add_printer_entry(ads, prt_dn, ctx, &mods);
187 if (!ADS_ERR_OK(ads_rc)) {
188 DEBUG(3, ("error publishing %s: %s\n",
189 printer, ads_errstr(ads_rc)));
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);
202 TALLOC_FREE(ctx);
204 return win_rc;
207 static WERROR nt_printer_unpublish_ads(ADS_STRUCT *ads,
208 const char *printer)
210 ADS_STATUS ads_rc;
211 LDAPMessage *res = NULL;
212 char *prt_dn = NULL;
214 DEBUG(5, ("unpublishing printer %s\n", printer));
216 /* remove the printer from the directory */
217 ads_rc = ads_find_printer_on_server(ads, &res,
218 printer, global_myname());
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;
226 ads_rc = ads_del_dn(ads, prt_dn);
227 TALLOC_FREE(prt_dn);
230 if (res) {
231 ads_msgfree(ads, res);
233 return WERR_OK;
236 /****************************************************************************
237 * Publish a printer in the directory
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 ***************************************************************************/
246 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
247 const struct auth_serversupplied_info *session_info,
248 struct messaging_context *msg_ctx,
249 struct spoolss_PrinterInfo2 *pinfo2,
250 int action)
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;
258 sinfo2 = talloc_zero(mem_ctx, struct spoolss_SetPrinterInfo2);
259 if (!sinfo2) {
260 return WERR_NOMEM;
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;
276 sinfo2->attributes = pinfo2->attributes;
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;
286 TALLOC_FREE(sinfo2);
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;
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);
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;
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;
317 done:
318 ads_destroy(&ads);
319 return win_rc;
322 WERROR check_published_printers(struct messaging_context *msg_ctx)
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_serversupplied_info *session_info = NULL;
330 struct spoolss_PrinterInfo2 *pinfo2;
331 NTSTATUS status;
332 WERROR result;
334 tmp_ctx = talloc_new(NULL);
335 if (!tmp_ctx) return WERR_NOMEM;
337 ads = ads_init(lp_realm(), lp_workgroup(), NULL);
338 if (!ads) {
339 DEBUG(3, ("ads_init() failed\n"));
340 return WERR_SERVER_UNAVAILABLE;
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);
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;
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;
363 for (snum = 0; snum < n_services; snum++) {
364 if (!lp_snum_ok(snum) || !lp_print_ok(snum)) {
365 continue;
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;
375 if (pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
376 nt_printer_publish_ads(msg_ctx, ads, pinfo2);
379 TALLOC_FREE(pinfo2);
382 result = WERR_OK;
383 done:
384 ads_destroy(&ads);
385 ads_kdestroy("MEMORY:prtpub_cache");
386 talloc_free(tmp_ctx);
387 return result;
390 bool is_printer_published(TALLOC_CTX *mem_ctx,
391 const struct auth_serversupplied_info *session_info,
392 struct messaging_context *msg_ctx,
393 const char *servername, char *printer, struct GUID *guid,
394 struct spoolss_PrinterInfo2 **info2)
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;
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;
412 result = winreg_get_printer(mem_ctx, b,
413 printer, &pinfo2);
414 if (!W_ERROR_IS_OK(result)) {
415 return false;
418 if (!(pinfo2->attributes & PRINTER_ATTRIBUTE_PUBLISHED)) {
419 TALLOC_FREE(pinfo2);
420 return false;
423 if (!guid) {
424 goto done;
427 /* fetching printer guids really ought to be a separate function. */
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;
438 /* We used to store the guid as REG_BINARY, then swapped
439 to REG_SZ for Vista compatibility so check for both */
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;
448 break;
450 case REG_BINARY:
451 if (data_size != sizeof(struct GUID)) {
452 TALLOC_FREE(pinfo2);
453 return false;
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;
463 done:
464 if (info2) {
465 *info2 = talloc_move(mem_ctx, &pinfo2);
467 talloc_free(pinfo2);
468 return true;
470 #else
471 WERROR nt_printer_publish(TALLOC_CTX *mem_ctx,
472 const struct auth_serversupplied_info *session_info,
473 struct messaging_context *msg_ctx,
474 struct spoolss_PrinterInfo2 *pinfo2,
475 int action)
477 return WERR_OK;
480 WERROR check_published_printers(struct messaging_context *msg_ctx)
482 return WERR_OK;
485 bool is_printer_published(TALLOC_CTX *mem_ctx,
486 const struct auth_serversupplied_info *session_info,
487 struct messaging_context *msg_ctx,
488 const char *servername, char *printer, struct GUID *guid,
489 struct spoolss_PrinterInfo2 **info2)
491 return False;
493 #endif /* HAVE_ADS */