s3-spoolss: fix printer driver version deletion
authorDavid Disseldorp <ddiss@samba.org>
Thu, 12 Jan 2012 15:27:37 +0000 (16:27 +0100)
committerDavid Disseldorp <ddiss@samba.org>
Fri, 20 Jan 2012 16:43:50 +0000 (17:43 +0100)
Spoolss delete printer driver code currently makes invalid version
assumptions based on the architecture requested by the client.

Ugly hacks are in place to cover removal of other versions (2 and 3).
This change wraps multi version deletion in a simple for loop.

Signed-off-by: Andreas Schneider <asn@samba.org>
source3/rpc_server/spoolss/srv_spoolss_nt.c

index 7a51703c01918ff0362945a4dbc5b521984fadb8..07a9f826208609ba346aeb3e69f73d289144a81a 100644 (file)
@@ -2039,6 +2039,12 @@ static const struct print_architecture_table_node archi_table[]= {
        {NULL,                   "",            -1 }
 };
 
+static const int drv_cversion[] = {SPOOLSS_DRIVER_VERSION_9X,
+                                  SPOOLSS_DRIVER_VERSION_NT35,
+                                  SPOOLSS_DRIVER_VERSION_NT4,
+                                  SPOOLSS_DRIVER_VERSION_200X,
+                                  -1};
+
 static int get_version_id(const char *arch)
 {
        int i;
@@ -2061,11 +2067,12 @@ WERROR _spoolss_DeletePrinterDriver(struct pipes_struct *p,
 {
 
        struct spoolss_DriverInfo8 *info = NULL;
-       struct spoolss_DriverInfo8 *info_win2k = NULL;
        int                             version;
        WERROR                          status;
        struct dcerpc_binding_handle *b;
        TALLOC_CTX *tmp_ctx = NULL;
+       int i;
+       bool found;
 
        /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
           and not a printer admin, then fail */
@@ -2101,64 +2108,98 @@ WERROR _spoolss_DeletePrinterDriver(struct pipes_struct *p,
                goto done;
        }
 
-       status = winreg_get_driver(tmp_ctx, b,
-                                  r->in.architecture, r->in.driver,
-                                  version, &info);
-       if (!W_ERROR_IS_OK(status)) {
-               /* try for Win2k driver if "Windows NT x86" */
-
-               if ( version == 2 ) {
-                       version = 3;
+       for (found = false, i = 0; drv_cversion[i] >= 0; i++) {
+               status = winreg_get_driver(tmp_ctx, b,
+                                          r->in.architecture, r->in.driver,
+                                          drv_cversion[i], &info);
+               if (!W_ERROR_IS_OK(status)) {
+                       DEBUG(5, ("skipping del of driver with version %d\n",
+                                 drv_cversion[i]));
+                       continue;
+               }
+               found = true;
 
-                       status = winreg_get_driver(tmp_ctx, b,
-                                                  r->in.architecture,
-                                                  r->in.driver,
-                                                  version, &info);
-                       if (!W_ERROR_IS_OK(status)) {
-                               status = WERR_UNKNOWN_PRINTER_DRIVER;
-                               goto done;
-                       }
+               if (printer_driver_in_use(tmp_ctx, b, info)) {
+                       status = WERR_PRINTER_DRIVER_IN_USE;
+                       goto done;
                }
-               /* otherwise it was a failure */
-               else {
-                       status = WERR_UNKNOWN_PRINTER_DRIVER;
+
+               status = winreg_del_driver(tmp_ctx, b, info, drv_cversion[i]);
+               if (!W_ERROR_IS_OK(status)) {
+                       DEBUG(0, ("failed del of driver with version %d\n",
+                                 drv_cversion[i]));
                        goto done;
                }
+       }
+       if (found == false) {
+               DEBUG(0, ("driver %s not found for deletion\n", r->in.driver));
+               status = WERR_UNKNOWN_PRINTER_DRIVER;
+       } else {
+               status = WERR_OK;
+       }
+
+done:
+       talloc_free(tmp_ctx);
 
+       return status;
+}
+
+static WERROR spoolss_dpd_version(TALLOC_CTX *mem_ctx,
+                                 struct pipes_struct *p,
+                                 struct spoolss_DeletePrinterDriverEx *r,
+                                 struct dcerpc_binding_handle *b,
+                                 struct spoolss_DriverInfo8 *info)
+{
+       WERROR status;
+       bool delete_files;
+
+       if (printer_driver_in_use(mem_ctx, b, info)) {
+               status = WERR_PRINTER_DRIVER_IN_USE;
+               goto done;
        }
 
-       if (printer_driver_in_use(tmp_ctx,
-                                 b,
-                                 info)) {
+       /*
+        * we have a couple of cases to consider.
+        * (1) Are any files in use?  If so and DPD_DELETE_ALL_FILES is set,
+        *     then the delete should fail if **any** files overlap with
+        *     other drivers
+        * (2) If DPD_DELETE_UNUSED_FILES is set, then delete all
+        *     non-overlapping files
+        * (3) If neither DPD_DELETE_ALL_FILES nor DPD_DELETE_UNUSED_FILES
+        *     are set, then do not delete any files
+        * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+        */
+
+       delete_files = r->in.delete_flags
+                       & (DPD_DELETE_ALL_FILES | DPD_DELETE_UNUSED_FILES);
+
+       /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
+
+       if (delete_files &&
+           (r->in.delete_flags & DPD_DELETE_ALL_FILES) &&
+           printer_driver_files_in_use(mem_ctx,
+                                       b,
+                                       info)) {
                status = WERR_PRINTER_DRIVER_IN_USE;
                goto done;
        }
 
-       if (version == 2) {
-               status = winreg_get_driver(tmp_ctx, b,
-                                          r->in.architecture,
-                                          r->in.driver, 3, &info_win2k);
-               if (W_ERROR_IS_OK(status)) {
-                       /* if we get to here, we now have 2 driver info structures to remove */
-                       /* remove the Win2k driver first*/
-
-                       status = winreg_del_driver(tmp_ctx, b,
-                                                  info_win2k, 3);
-                       talloc_free(info_win2k);
-
-                       /* this should not have failed---if it did, report to client */
-                       if (!W_ERROR_IS_OK(status)) {
-                               goto done;
-                       }
-               }
+
+       status = winreg_del_driver(mem_ctx, b, info, info->version);
+       if (!W_ERROR_IS_OK(status)) {
+               goto done;
        }
 
-       status = winreg_del_driver(tmp_ctx, b,
-                                  info, version);
+       /*
+        * now delete any associated files if delete_files is
+        * true. Even if this part failes, we return succes
+        * because the driver doesn not exist any more
+        */
+       if (delete_files) {
+               delete_driver_files(get_session_info_system(), info);
+       }
 
 done:
-       talloc_free(tmp_ctx);
-
        return status;
 }
 
@@ -2169,13 +2210,12 @@ done:
 WERROR _spoolss_DeletePrinterDriverEx(struct pipes_struct *p,
                                      struct spoolss_DeletePrinterDriverEx *r)
 {
-       struct spoolss_DriverInfo8      *info = NULL;
-       struct spoolss_DriverInfo8      *info_win2k = NULL;
-       int                             version;
-       bool                            delete_files;
+       struct spoolss_DriverInfo8 *info = NULL;
        WERROR                          status;
        struct dcerpc_binding_handle *b;
        TALLOC_CTX *tmp_ctx = NULL;
+       int i;
+       bool found;
 
        /* if the user is not root, doesn't have SE_PRINT_OPERATOR privilege,
           and not a printer admin, then fail */
@@ -2192,15 +2232,11 @@ WERROR _spoolss_DeletePrinterDriverEx(struct pipes_struct *p,
        }
 
        /* check that we have a valid driver name first */
-       if ((version = get_version_id(r->in.architecture)) == -1) {
+       if (get_version_id(r->in.architecture) == -1) {
                /* this is what NT returns */
                return WERR_INVALID_ENVIRONMENT;
        }
 
-       if (r->in.delete_flags & DPD_DELETE_SPECIFIC_VERSION) {
-               version = r->in.version;
-       }
-
        tmp_ctx = talloc_new(p->mem_ctx);
        if (!tmp_ctx) {
                return WERR_NOMEM;
@@ -2214,127 +2250,35 @@ WERROR _spoolss_DeletePrinterDriverEx(struct pipes_struct *p,
                goto done;
        }
 
-       status = winreg_get_driver(tmp_ctx, b,
-                                  r->in.architecture,
-                                  r->in.driver,
-                                  version,
-                                  &info);
-       if (!W_ERROR_IS_OK(status)) {
-               status = WERR_UNKNOWN_PRINTER_DRIVER;
-
-               /*
-                * if the client asked for a specific version,
-                * or this is something other than Windows NT x86,
-                * then we've failed
-                */
-
-               if ( (r->in.delete_flags & DPD_DELETE_SPECIFIC_VERSION) || (version !=2) )
-                       goto done;
-
-               /* try for Win2k driver if "Windows NT x86" */
+       for (found = false, i = 0; drv_cversion[i] >= 0; i++) {
+               if ((r->in.delete_flags & DPD_DELETE_SPECIFIC_VERSION)
+                && (drv_cversion[i] != r->in.version)) {
+                       continue;
+               }
 
-               version = 3;
+               /* check if a driver with this version exists before delete */
                status = winreg_get_driver(tmp_ctx, b,
-                                          r->in.architecture,
-                                          r->in.driver,
-                                          version, &info);
+                                          r->in.architecture, r->in.driver,
+                                          drv_cversion[i], &info);
                if (!W_ERROR_IS_OK(status)) {
-                       status = WERR_UNKNOWN_PRINTER_DRIVER;
-                       goto done;
+                       DEBUG(5, ("skipping del of driver with version %d\n",
+                                 drv_cversion[i]));
+                       continue;
                }
-       }
-
-       if (printer_driver_in_use(tmp_ctx,
-                                 b,
-                                 info)) {
-               status = WERR_PRINTER_DRIVER_IN_USE;
-               goto done;
-       }
-
-       /*
-        * we have a couple of cases to consider.
-        * (1) Are any files in use?  If so and DPD_DELTE_ALL_FILE is set,
-        *     then the delete should fail if **any** files overlap with
-        *     other drivers
-        * (2) If DPD_DELTE_UNUSED_FILES is sert, then delete all
-        *     non-overlapping files
-        * (3) If neither DPD_DELTE_ALL_FILE nor DPD_DELTE_ALL_FILES
-        *     is set, the do not delete any files
-        * Refer to MSDN docs on DeletePrinterDriverEx() for details.
-        */
-
-       delete_files = r->in.delete_flags & (DPD_DELETE_ALL_FILES|DPD_DELETE_UNUSED_FILES);
-
-       /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
-
-       if (delete_files &&
-           (r->in.delete_flags & DPD_DELETE_ALL_FILES) &&
-           printer_driver_files_in_use(tmp_ctx,
-                                       b,
-                                       info)) {
-               status = WERR_PRINTER_DRIVER_IN_USE;
-               goto done;
-       }
-
-
-       /* also check for W32X86/3 if necessary; maybe we already have? */
-
-       if ( (version == 2) && ((r->in.delete_flags & DPD_DELETE_SPECIFIC_VERSION) != DPD_DELETE_SPECIFIC_VERSION)  ) {
-               status = winreg_get_driver(tmp_ctx, b,
-                                          r->in.architecture,
-                                          r->in.driver, 3, &info_win2k);
-               if (W_ERROR_IS_OK(status)) {
-
-                       if (delete_files &&
-                           (r->in.delete_flags & DPD_DELETE_ALL_FILES) &&
-                           printer_driver_files_in_use(info,
-                                                       b,
-                                                       info_win2k)) {
-                               /* no idea of the correct error here */
-                               talloc_free(info_win2k);
-                               status = WERR_ACCESS_DENIED;
-                               goto done;
-                       }
-
-                       /* if we get to here, we now have 2 driver info structures to remove */
-                       /* remove the Win2k driver first*/
-
-                       status = winreg_del_driver(tmp_ctx, b,
-                                                  info_win2k,
-                                                  3);
-
-                       /* this should not have failed---if it did, report to client */
-
-                       if (!W_ERROR_IS_OK(status)) {
-                               goto done;
-                       }
+               found = true;
 
-                       /*
-                        * now delete any associated files if delete_files is
-                        * true. Even if this part failes, we return succes
-                        * because the driver doesn not exist any more
-                        */
-                       if (delete_files) {
-                               delete_driver_files(get_session_info_system(),
-                                                   info_win2k);
-                       }
+               status = spoolss_dpd_version(tmp_ctx, p, r, b, info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("failed to delete driver with version %d\n",
+                                 drv_cversion[i]));
+                       goto done;
                }
        }
-
-       status = winreg_del_driver(tmp_ctx, b,
-                                  info,
-                                  version);
-       if (!W_ERROR_IS_OK(status)) {
-               goto done;
-       }
-
-       /*
-        * now delete any associated files if delete_files is
-        * true. Even if this part failes, we return succes
-        * because the driver doesn not exist any more
-        */
-       if (delete_files) {
-               delete_driver_files(get_session_info_system(), info);
+       if (found == false) {
+               DEBUG(0, ("driver %s not found for deletion\n", r->in.driver));
+               status = WERR_UNKNOWN_PRINTER_DRIVER;
+       } else {
+               status = WERR_OK;
        }
 
 done: