Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/rprn.py : 93%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Author: Alberto Solino (@agsolino)
8#
9# Description:
10# [MS-RPRN] Interface implementation
11#
12# Best way to learn how to use these calls is to grab the protocol standard
13# so you understand what the call does, and then read the test case located
14# at https://github.com/SecureAuthCorp/impacket/tree/master/tests/SMB_RPC
15#
16# Some calls have helper functions, which makes it even easier to use.
17# They are located at the end of this file.
18# Helper functions start with "h"<name of the call>.
19# There are test cases for them too.
20#
21from impacket import system_errors
22from impacket.dcerpc.v5.dtypes import ULONGLONG, UINT, USHORT, LPWSTR, DWORD, ULONG, NULL
23from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRUNION, NDRPOINTER, NDRUniConformantArray
24from impacket.dcerpc.v5.rpcrt import DCERPCException
25from impacket.uuid import uuidtup_to_bin
27MSRPC_UUID_RPRN = uuidtup_to_bin(('12345678-1234-ABCD-EF00-0123456789AB', '1.0'))
29class DCERPCSessionError(DCERPCException):
30 def __init__(self, error_string=None, error_code=None, packet=None):
31 DCERPCException.__init__(self, error_string, error_code, packet)
33 def __str__( self ):
34 key = self.error_code
35 if key in system_errors.ERROR_MESSAGES: 35 ↛ 40line 35 didn't jump to line 40, because the condition on line 35 was never false
36 error_msg_short = system_errors.ERROR_MESSAGES[key][0]
37 error_msg_verbose = system_errors.ERROR_MESSAGES[key][1]
38 return 'RPRN SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
39 else:
40 return 'RPRN SessionError: unknown error code: 0x%x' % self.error_code
42################################################################################
43# CONSTANTS
44################################################################################
45# 2.2.1.1.7 STRING_HANDLE
46STRING_HANDLE = LPWSTR
47class PSTRING_HANDLE(NDRPOINTER):
48 referent = (
49 ('Data', STRING_HANDLE),
50 )
52# 2.2.3.1 Access Values
53JOB_ACCESS_ADMINISTER = 0x00000010
54JOB_ACCESS_READ = 0x00000020
55JOB_EXECUTE = 0x00020010
56JOB_READ = 0x00020020
57JOB_WRITE = 0x00020010
58JOB_ALL_ACCESS = 0x000F0030
59PRINTER_ACCESS_ADMINISTER = 0x00000004
60PRINTER_ACCESS_USE = 0x00000008
61PRINTER_ACCESS_MANAGE_LIMITED = 0x00000040
62PRINTER_ALL_ACCESS = 0x000F000C
63PRINTER_EXECUTE = 0x00020008
64PRINTER_READ = 0x00020008
65PRINTER_WRITE = 0x00020008
66SERVER_ACCESS_ADMINISTER = 0x00000001
67SERVER_ACCESS_ENUMERATE = 0x00000002
68SERVER_ALL_ACCESS = 0x000F0003
69SERVER_EXECUTE = 0x00020002
70SERVER_READ = 0x00020002
71SERVER_WRITE = 0x00020003
72SPECIFIC_RIGHTS_ALL = 0x0000FFFF
73STANDARD_RIGHTS_ALL = 0x001F0000
74STANDARD_RIGHTS_EXECUTE = 0x00020000
75STANDARD_RIGHTS_READ = 0x00020000
76STANDARD_RIGHTS_REQUIRED = 0x000F0000
77STANDARD_RIGHTS_WRITE = 0x00020000
78SYNCHRONIZE = 0x00100000
79DELETE = 0x00010000
80READ_CONTROL = 0x00020000
81WRITE_DAC = 0x00040000
82WRITE_OWNER = 0x00080000
83GENERIC_READ = 0x80000000
84GENERIC_WRITE = 0x40000000
85GENERIC_EXECUTE = 0x20000000
86GENERIC_ALL = 0x10000000
88# 2.2.3.6.1 Printer Change Flags for Use with a Printer Handle
89PRINTER_CHANGE_SET_PRINTER = 0x00000002
90PRINTER_CHANGE_DELETE_PRINTER = 0x00000004
91PRINTER_CHANGE_PRINTER = 0x000000FF
92PRINTER_CHANGE_ADD_JOB = 0x00000100
93PRINTER_CHANGE_SET_JOB = 0x00000200
94PRINTER_CHANGE_DELETE_JOB = 0x00000400
95PRINTER_CHANGE_WRITE_JOB = 0x00000800
96PRINTER_CHANGE_JOB = 0x0000FF00
97PRINTER_CHANGE_SET_PRINTER_DRIVER = 0x20000000
98PRINTER_CHANGE_TIMEOUT = 0x80000000
99PRINTER_CHANGE_ALL = 0x7777FFFF
100PRINTER_CHANGE_ALL_2 = 0x7F77FFFF
102# 2.2.3.6.2 Printer Change Flags for Use with a Server Handle
103PRINTER_CHANGE_ADD_PRINTER_DRIVER = 0x10000000
104PRINTER_CHANGE_DELETE_PRINTER_DRIVER = 0x40000000
105PRINTER_CHANGE_PRINTER_DRIVER = 0x70000000
106PRINTER_CHANGE_ADD_FORM = 0x00010000
107PRINTER_CHANGE_DELETE_FORM = 0x00040000
108PRINTER_CHANGE_SET_FORM = 0x00020000
109PRINTER_CHANGE_FORM = 0x00070000
110PRINTER_CHANGE_ADD_PORT = 0x00100000
111PRINTER_CHANGE_CONFIGURE_PORT = 0x00200000
112PRINTER_CHANGE_DELETE_PORT = 0x00400000
113PRINTER_CHANGE_PORT = 0x00700000
114PRINTER_CHANGE_ADD_PRINT_PROCESSOR = 0x01000000
115PRINTER_CHANGE_DELETE_PRINT_PROCESSOR = 0x04000000
116PRINTER_CHANGE_PRINT_PROCESSOR = 0x07000000
117PRINTER_CHANGE_ADD_PRINTER = 0x00000001
118PRINTER_CHANGE_FAILED_CONNECTION_PRINTER = 0x00000008
119PRINTER_CHANGE_SERVER = 0x08000000
121# 2.2.3.7 Printer Enumeration Flags
122PRINTER_ENUM_LOCAL = 0x00000002
123PRINTER_ENUM_CONNECTIONS = 0x00000004
124PRINTER_ENUM_NAME = 0x00000008
125PRINTER_ENUM_REMOTE = 0x00000010
126PRINTER_ENUM_SHARED = 0x00000020
127PRINTER_ENUM_NETWORK = 0x00000040
128PRINTER_ENUM_EXPAND = 0x00004000
129PRINTER_ENUM_CONTAINER = 0x00008000
130PRINTER_ENUM_ICON1 = 0x00010000
131PRINTER_ENUM_ICON2 = 0x00020000
132PRINTER_ENUM_ICON3 = 0x00040000
133PRINTER_ENUM_ICON8 = 0x00800000
134PRINTER_ENUM_HIDE = 0x01000000
137# 2.2.3.8 Printer Notification Values
138PRINTER_NOTIFY_CATEGORY_2D = 0x00000000
139PRINTER_NOTIFY_CATEGORY_ALL = 0x00010000
140PRINTER_NOTIFY_CATEGORY_3D = 0x00020000
143################################################################################
144# STRUCTURES
145################################################################################
146# 2.2.1.1.4 PRINTER_HANDLE
147class PRINTER_HANDLE(NDRSTRUCT):
148 structure = (
149 ('Data','20s=b""'),
150 )
151 def getAlignment(self):
152 if self._isNDR64 is True:
153 return 8
154 else:
155 return 4
157# 2.2.1.2.1 DEVMODE_CONTAINER
158class BYTE_ARRAY(NDRUniConformantArray):
159 item = 'c'
161class PBYTE_ARRAY(NDRPOINTER):
162 referent = (
163 ('Data', BYTE_ARRAY),
164 )
166class DEVMODE_CONTAINER(NDRSTRUCT):
167 structure = (
168 ('cbBuf',DWORD),
169 ('pDevMode',PBYTE_ARRAY),
170 )
172# 2.2.1.11.1 SPLCLIENT_INFO_1
173class SPLCLIENT_INFO_1(NDRSTRUCT):
174 structure = (
175 ('dwSize',DWORD),
176 ('pMachineName',LPWSTR),
177 ('pUserName',LPWSTR),
178 ('dwBuildNum',DWORD),
179 ('dwMajorVersion',DWORD),
180 ('dwMinorVersion',DWORD),
181 ('wProcessorArchitecture',USHORT),
182 )
184class PSPLCLIENT_INFO_1(NDRPOINTER):
185 referent = (
186 ('Data', SPLCLIENT_INFO_1),
187 )
189# 2.2.1.11.2 SPLCLIENT_INFO_2
190class SPLCLIENT_INFO_2(NDRSTRUCT):
191 structure = (
192 ('notUsed',ULONGLONG),
193 )
195class PSPLCLIENT_INFO_2(NDRPOINTER):
196 referent = (
197 ('Data', SPLCLIENT_INFO_2),
198 )
199# 2.2.1.11.3 SPLCLIENT_INFO_3
200class SPLCLIENT_INFO_3(NDRSTRUCT):
201 structure = (
202 ('cbSize',UINT),
203 ('dwFlags',DWORD),
204 ('dwFlags',DWORD),
205 ('pMachineName',LPWSTR),
206 ('pUserName',LPWSTR),
207 ('dwBuildNum',DWORD),
208 ('dwMajorVersion',DWORD),
209 ('dwMinorVersion',DWORD),
210 ('wProcessorArchitecture',USHORT),
211 ('hSplPrinter',ULONGLONG),
212 )
214class PSPLCLIENT_INFO_3(NDRPOINTER):
215 referent = (
216 ('Data', SPLCLIENT_INFO_3),
217 )
218# 2.2.1.2.14 SPLCLIENT_CONTAINER
219class CLIENT_INFO_UNION(NDRUNION):
220 commonHdr = (
221 ('tag', ULONG),
222 )
223 union = {
224 1 : ('pClientInfo1', PSPLCLIENT_INFO_1),
225 2 : ('pNotUsed1', PSPLCLIENT_INFO_2),
226 3 : ('pNotUsed2', PSPLCLIENT_INFO_3),
227 }
229class SPLCLIENT_CONTAINER(NDRSTRUCT):
230 structure = (
231 ('Level',DWORD),
232 ('ClientInfo',CLIENT_INFO_UNION),
233 )
236# 2.2.1.13.2 RPC_V2_NOTIFY_OPTIONS_TYPE
237class USHORT_ARRAY(NDRUniConformantArray):
238 item = '<H'
240class PUSHORT_ARRAY(NDRPOINTER):
241 referent = (
242 ('Data', USHORT_ARRAY),
243 )
245class RPC_V2_NOTIFY_OPTIONS_TYPE(NDRSTRUCT):
246 structure = (
247 ('Type',USHORT),
248 ('Reserved0',USHORT),
249 ('Reserved1',DWORD),
250 ('Reserved2',DWORD),
251 ('Count',DWORD),
252 ('pFields',PUSHORT_ARRAY),
253 )
255class PRPC_V2_NOTIFY_OPTIONS_TYPE_ARRAY(NDRPOINTER):
256 referent = (
257 ('Data', RPC_V2_NOTIFY_OPTIONS_TYPE),
258 )
260# 2.2.1.13.1 RPC_V2_NOTIFY_OPTIONS
261class RPC_V2_NOTIFY_OPTIONS(NDRSTRUCT):
262 structure = (
263 ('Version',DWORD),
264 ('Reserved',DWORD),
265 ('Count',DWORD),
266 ('pTypes',PRPC_V2_NOTIFY_OPTIONS_TYPE_ARRAY),
267 )
269class PRPC_V2_NOTIFY_OPTIONS(NDRPOINTER):
270 referent = (
271 ('Data', RPC_V2_NOTIFY_OPTIONS),
272 )
275################################################################################
276# RPC CALLS
277################################################################################
278# 3.1.4.2.1 RpcEnumPrinters (Opnum 0)
279class RpcEnumPrinters(NDRCALL):
280 opnum = 0
281 structure = (
282 ('Flags', DWORD),
283 ('Name', STRING_HANDLE),
284 ('Level', DWORD),
285 ('pPrinterEnum', PBYTE_ARRAY),
286 ('cbBuf', DWORD),
287 )
289class RpcEnumPrintersResponse(NDRCALL):
290 structure = (
291 ('pPrinterEnum', PBYTE_ARRAY),
292 ('pcbNeeded', DWORD),
293 ('pcReturned', DWORD),
294 ('ErrorCode', ULONG),
295 )
296# 3.1.4.2.2 RpcOpenPrinter (Opnum 1)
297class RpcOpenPrinter(NDRCALL):
298 opnum = 1
299 structure = (
300 ('pPrinterName', STRING_HANDLE),
301 ('pDatatype', LPWSTR),
302 ('pDevModeContainer', DEVMODE_CONTAINER),
303 ('AccessRequired', DWORD),
304 )
306class RpcOpenPrinterResponse(NDRCALL):
307 structure = (
308 ('pHandle', PRINTER_HANDLE),
309 ('ErrorCode', ULONG),
310 )
312# 3.1.4.2.9 RpcClosePrinter (Opnum 29)
313class RpcClosePrinter(NDRCALL):
314 opnum = 29
315 structure = (
316 ('phPrinter', PRINTER_HANDLE),
317 )
319class RpcClosePrinterResponse(NDRCALL):
320 structure = (
321 ('phPrinter', PRINTER_HANDLE),
322 ('ErrorCode', ULONG),
323 )
325# 3.1.4.10.4 RpcRemoteFindFirstPrinterChangeNotificationEx (Opnum 65)
326class RpcRemoteFindFirstPrinterChangeNotificationEx(NDRCALL):
327 opnum = 65
328 structure = (
329 ('hPrinter', PRINTER_HANDLE),
330 ('fdwFlags', DWORD),
331 ('fdwOptions', DWORD),
332 ('pszLocalMachine', LPWSTR),
333 ('dwPrinterLocal', DWORD),
334 ('pOptions', PRPC_V2_NOTIFY_OPTIONS),
335 )
337class RpcRemoteFindFirstPrinterChangeNotificationExResponse(NDRCALL):
338 structure = (
339 ('ErrorCode', ULONG),
340 )
342# 3.1.4.2.14 RpcOpenPrinterEx (Opnum 69)
343class RpcOpenPrinterEx(NDRCALL):
344 opnum = 69
345 structure = (
346 ('pPrinterName', STRING_HANDLE),
347 ('pDatatype', LPWSTR),
348 ('pDevModeContainer', DEVMODE_CONTAINER),
349 ('AccessRequired', DWORD),
350 ('pClientInfo', SPLCLIENT_CONTAINER),
351 )
353class RpcOpenPrinterExResponse(NDRCALL):
354 structure = (
355 ('pHandle', PRINTER_HANDLE),
356 ('ErrorCode', ULONG),
357 )
359################################################################################
360# OPNUMs and their corresponding structures
361################################################################################
362OPNUMS = {
363 0 : (RpcEnumPrinters, RpcEnumPrintersResponse),
364 1 : (RpcOpenPrinter, RpcOpenPrinterResponse),
365 29 : (RpcClosePrinter, RpcClosePrinterResponse),
366 65 : (RpcRemoteFindFirstPrinterChangeNotificationEx, RpcRemoteFindFirstPrinterChangeNotificationExResponse),
367 69 : (RpcOpenPrinterEx, RpcOpenPrinterExResponse),
368}
370################################################################################
371# HELPER FUNCTIONS
372################################################################################
373def checkNullString(string):
374 if string == NULL: 374 ↛ 375line 374 didn't jump to line 375, because the condition on line 374 was never true
375 return string
377 if string[-1:] != '\x00': 377 ↛ 378line 377 didn't jump to line 378, because the condition on line 377 was never true
378 return string + '\x00'
379 else:
380 return string
382def hRpcOpenPrinter(dce, printerName, pDatatype = NULL, pDevModeContainer = NULL, accessRequired = SERVER_READ):
383 """
384 RpcOpenPrinter retrieves a handle for a printer, port, port monitor, print job, or print server.
385 Full Documentation: https://msdn.microsoft.com/en-us/library/cc244808.aspx
387 :param DCERPC_v5 dce: a connected DCE instance.
388 :param string printerName: A string for a printer connection, printer object, server object, job object, port
389 object, or port monitor object. This MUST be a Domain Name System (DNS), NetBIOS, Internet Protocol version 4
390 (IPv4), Internet Protocol version 6 (IPv6), or Universal Naming Convention (UNC) name that remote procedure
391 call (RPC) binds to, and it MUST uniquely identify a print server on the network.
392 :param string pDatatype: A string that specifies the data type to be associated with the printer handle.
393 :param DEVMODE_CONTAINER pDevModeContainer: A DEVMODE_CONTAINER structure. This parameter MUST adhere to the specification in
394 DEVMODE_CONTAINER Parameters (section 3.1.4.1.8.1).
395 :param int accessRequired: The access level that the client requires for interacting with the object to which a
396 handle is being opened.
398 :return: a RpcOpenPrinterResponse instance, raises DCERPCSessionError on error.
399 """
400 request = RpcOpenPrinter()
401 request['pPrinterName'] = checkNullString(printerName)
402 request['pDatatype'] = pDatatype
403 if pDevModeContainer is NULL: 403 ↛ 406line 403 didn't jump to line 406, because the condition on line 403 was never false
404 request['pDevModeContainer']['pDevMode'] = NULL
405 else:
406 request['pDevModeContainer'] = pDevModeContainer
408 request['AccessRequired'] = accessRequired
409 return dce.request(request)
411def hRpcClosePrinter(dce, phPrinter):
412 """
413 RpcClosePrinter closes a handle to a printer object, server object, job object, or port object.
414 Full Documentation: https://msdn.microsoft.com/en-us/library/cc244768.aspx
416 :param DCERPC_v5 dce: a connected DCE instance.
417 :param PRINTER_HANDLE phPrinter: A handle to a printer object, server object, job object, or port object.
419 :return: a RpcClosePrinterResponse instance, raises DCERPCSessionError on error.
420 """
421 request = RpcClosePrinter()
422 request['phPrinter'] = phPrinter
423 return dce.request(request)
426def hRpcOpenPrinterEx(dce, printerName, pDatatype=NULL, pDevModeContainer=NULL, accessRequired=SERVER_READ,
427 pClientInfo=NULL):
428 """
429 RpcOpenPrinterEx retrieves a handle for a printer, port, port monitor, print job, or print server
430 Full Documentation: https://msdn.microsoft.com/en-us/library/cc244809.aspx
432 :param DCERPC_v5 dce: a connected DCE instance.
433 :param string printerName: A string for a printer connection, printer object, server object, job object, port
434 object, or port monitor object. This MUST be a Domain Name System (DNS), NetBIOS, Internet Protocol version 4
435 (IPv4), Internet Protocol version 6 (IPv6), or Universal Naming Convention (UNC) name that remote procedure
436 call (RPC) binds to, and it MUST uniquely identify a print server on the network.
437 :param string pDatatype: A string that specifies the data type to be associated with the printer handle.
438 :param DEVMODE_CONTAINER pDevModeContainer: A DEVMODE_CONTAINER structure. This parameter MUST adhere to the specification in
439 DEVMODE_CONTAINER Parameters (section 3.1.4.1.8.1).
440 :param int accessRequired: The access level that the client requires for interacting with the object to which a
441 handle is being opened.
442 :param SPLCLIENT_CONTAINER pClientInfo: This parameter MUST adhere to the specification in SPLCLIENT_CONTAINER Parameters.
444 :return: a RpcOpenPrinterExResponse instance, raises DCERPCSessionError on error.
445 """
446 request = RpcOpenPrinterEx()
447 request['pPrinterName'] = checkNullString(printerName)
448 request['pDatatype'] = pDatatype
449 if pDevModeContainer is NULL: 449 ↛ 452line 449 didn't jump to line 452, because the condition on line 449 was never false
450 request['pDevModeContainer']['pDevMode'] = NULL
451 else:
452 request['pDevModeContainer'] = pDevModeContainer
454 request['AccessRequired'] = accessRequired
455 if pClientInfo is NULL: 455 ↛ 456line 455 didn't jump to line 456, because the condition on line 455 was never true
456 raise Exception('pClientInfo cannot be NULL')
458 request['pClientInfo'] = pClientInfo
459 return dce.request(request)
462def hRpcRemoteFindFirstPrinterChangeNotificationEx(dce, hPrinter, fdwFlags, fdwOptions=0, pszLocalMachine=NULL,
463 dwPrinterLocal=0, pOptions=NULL):
464 """
465 creates a remote change notification object that monitors changes to printer objects and sends change notifications
466 to a print client using either RpcRouterReplyPrinter (section 3.2.4.1.2) or RpcRouterReplyPrinterEx (section 3.2.4.1.4)
467 Full Documentation: https://msdn.microsoft.com/en-us/library/cc244813.aspx
469 :param DCERPC_v5 dce: a connected DCE instance.
470 :param PRINTER_HANDLE hPrinter: A handle to a printer or server object.
471 :param int fdwFlags: Flags that specify the conditions that are required for a change notification object to enter a signaled state.
472 :param int fdwOptions: The category of printers for which change notifications are returned.
473 :param string pszLocalMachine: A string that represents the name of the client computer.
474 :param int dwPrinterLocal: An implementation-specific unique value that MUST be sufficient for the client to determine
475 whether a call to RpcReplyOpenPrinter by the server is associated with the hPrinter parameter in this call.
476 :param RPC_V2_NOTIFY_OPTIONS pOptions: An RPC_V2_NOTIFY_OPTIONS structure that specifies printer or job members that the client listens to for notifications.
478 :return: a RpcRemoteFindFirstPrinterChangeNotificationExResponse instance, raises DCERPCSessionError on error.
479 """
480 request = RpcRemoteFindFirstPrinterChangeNotificationEx()
482 request['hPrinter'] = hPrinter
483 request['fdwFlags'] = fdwFlags
484 request['fdwOptions'] = fdwOptions
485 request['dwPrinterLocal'] = dwPrinterLocal
486 if pszLocalMachine is NULL: 486 ↛ 487line 486 didn't jump to line 487, because the condition on line 486 was never true
487 raise Exception('pszLocalMachine cannot be NULL')
488 request['pszLocalMachine'] = checkNullString(pszLocalMachine)
489 request['pOptions'] = pOptions
490 return dce.request(request)
492def hRpcEnumPrinters(dce, flags, name = NULL, level = 1):
493 """
494 RpcEnumPrinters enumerates available printers, print servers, domains, or print providers.
495 Full Documentation: https://msdn.microsoft.com/en-us/library/cc244794.aspx
497 :param DCERPC_v5 dce: a connected DCE instance.
498 :param int flags: The types of print objects that this method enumerates. The value of this parameter is the
499 result of a bitwise OR of one or more of the Printer Enumeration Flags (section 2.2.3.7).
500 :param string name: NULL or a server name parameter as specified in Printer Server Name Parameters (section 3.1.4.1.4).
501 :param level: The level of printer information structure.
503 :return: a RpcEnumPrintersResponse instance, raises DCERPCSessionError on error.
504 """
505 request = RpcEnumPrinters()
506 request['Flags'] = flags
507 request['Name'] = name
508 request['pPrinterEnum'] = NULL
509 request['Level'] = level
510 bytesNeeded = 0
511 try:
512 dce.request(request)
513 except DCERPCSessionError as e:
514 if str(e).find('ERROR_INSUFFICIENT_BUFFER') < 0: 514 ↛ 515line 514 didn't jump to line 515, because the condition on line 514 was never true
515 raise
516 bytesNeeded = e.get_packet()['pcbNeeded']
518 request = RpcEnumPrinters()
519 request['Flags'] = flags
520 request['Name'] = name
521 request['Level'] = level
523 request['cbBuf'] = bytesNeeded
524 request['pPrinterEnum'] = b'a' * bytesNeeded
525 return dce.request(request)