smartcard: developer checkin for smartcard support
This commit is contained in:
parent
cd0a8721d1
commit
89b7cd269e
@ -368,18 +368,18 @@ do \
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* copy data into stream */
|
/* copy data into stream */
|
||||||
#define xstream_copyin(_s, _dest, _len) \
|
#define xstream_copyin(_s, _dest, _len) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
g_memcpy((_s)->p, (_dest), (_len)); \
|
g_memcpy((_s)->p, (_dest), (_len)); \
|
||||||
(_s)->p += (_len); \
|
(_s)->p += (_len); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* copy data out of stream */
|
/* copy data out of stream */
|
||||||
#define xstream_copyout(_dest, _s, _len) \
|
#define xstream_copyout(_dest, _s, _len) \
|
||||||
{ \
|
|
||||||
do \
|
do \
|
||||||
g_memcpy((_dest), (_s)->p, (_len)); \
|
{ \
|
||||||
|
g_memcpy((_dest), (_s)->p, (_len)); \
|
||||||
(_s)->p += (_len); \
|
(_s)->p += (_len); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
@ -35,10 +35,12 @@ xrdp_chansrv_SOURCES = \
|
|||||||
clipboard.c \
|
clipboard.c \
|
||||||
clipboard_file.c \
|
clipboard_file.c \
|
||||||
devredir.c \
|
devredir.c \
|
||||||
|
smartcard.c \
|
||||||
rail.c \
|
rail.c \
|
||||||
xcommon.c \
|
xcommon.c \
|
||||||
drdynvc.c \
|
drdynvc.c \
|
||||||
chansrv_fuse.c
|
chansrv_fuse.c \
|
||||||
|
irp.c
|
||||||
|
|
||||||
xrdp_chansrv_LDFLAGS = \
|
xrdp_chansrv_LDFLAGS = \
|
||||||
$(EXTRA_FLAGS)
|
$(EXTRA_FLAGS)
|
||||||
|
@ -116,7 +116,7 @@ void xfuse_devredir_cb_file_close(void *vp) {}
|
|||||||
#define LOG_ERROR 0
|
#define LOG_ERROR 0
|
||||||
#define LOG_INFO 1
|
#define LOG_INFO 1
|
||||||
#define LOG_DEBUG 2
|
#define LOG_DEBUG 2
|
||||||
#define LOG_LEVEL LOG_ERROR
|
#define LOG_LEVEL LOG_DEBUG
|
||||||
|
|
||||||
#define log_error(_params...) \
|
#define log_error(_params...) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -34,7 +34,57 @@
|
|||||||
* o mark local funcs with static
|
* o mark local funcs with static
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
|
#include "parse.h"
|
||||||
|
#include "os_calls.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "chansrv_fuse.h"
|
||||||
#include "devredir.h"
|
#include "devredir.h"
|
||||||
|
#include "smartcard.h"
|
||||||
|
|
||||||
|
/* module based logging */
|
||||||
|
#define LOG_ERROR 0
|
||||||
|
#define LOG_INFO 1
|
||||||
|
#define LOG_DEBUG 2
|
||||||
|
|
||||||
|
#ifndef LOG_LEVEL
|
||||||
|
#define LOG_LEVEL LOG_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define log_error(_params...) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: DEV_REDIR %s: %d : ERROR: ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define log_info(_params...) \
|
||||||
|
{ \
|
||||||
|
if (LOG_INFO <= LOG_LEVEL) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: DEV_REDIR %s: %d : ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define log_debug(_params...) \
|
||||||
|
{ \
|
||||||
|
if (LOG_DEBUG <= LOG_LEVEL) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: DEV_REDIR %s: %d : ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
/* globals */
|
/* globals */
|
||||||
extern int g_rdpdr_chan_id; /* in chansrv.c */
|
extern int g_rdpdr_chan_id; /* in chansrv.c */
|
||||||
@ -43,14 +93,12 @@ int g_is_port_redir_supported = 0;
|
|||||||
int g_is_drive_redir_supported = 0;
|
int g_is_drive_redir_supported = 0;
|
||||||
int g_is_smartcard_redir_supported = 0;
|
int g_is_smartcard_redir_supported = 0;
|
||||||
int g_drive_redir_version = 1;
|
int g_drive_redir_version = 1;
|
||||||
char g_preferred_dos_name_for_filesystem[9];
|
|
||||||
char g_full_name_for_filesystem[1024];
|
char g_full_name_for_filesystem[1024];
|
||||||
tui32 g_completion_id = 1;
|
tui32 g_completion_id = 1;
|
||||||
|
|
||||||
tui32 g_clientID; /* unique client ID - announced by client */
|
tui32 g_clientID; /* unique client ID - announced by client */
|
||||||
tui32 g_device_id; /* unique device ID - announced by client */
|
tui32 g_device_id; /* unique device ID - announced by client */
|
||||||
tui16 g_client_rdp_version; /* returned by client */
|
tui16 g_client_rdp_version; /* returned by client */
|
||||||
IRP *g_irp_head = NULL;
|
|
||||||
struct stream *g_input_stream = NULL;
|
struct stream *g_input_stream = NULL;
|
||||||
|
|
||||||
void xfuse_devredir_cb_write_file(void *vp, char *buf, size_t length);
|
void xfuse_devredir_cb_write_file(void *vp, char *buf, size_t length);
|
||||||
@ -207,7 +255,7 @@ dev_redir_data_in(struct stream *s, int chan_id, int chan_flags, int length,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PAKID_CORE_DEVICELIST_ANNOUNCE:
|
case PAKID_CORE_DEVICELIST_ANNOUNCE:
|
||||||
dev_redir_proc_client_devlist_announce_req(ls);
|
devredir_proc_client_devlist_announce_req(ls);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PAKID_CORE_DEVICE_IOCOMPLETION:
|
case PAKID_CORE_DEVICE_IOCOMPLETION:
|
||||||
@ -347,7 +395,7 @@ void dev_redir_send_server_user_logged_on()
|
|||||||
xstream_free(s);
|
xstream_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dev_redir_send_server_device_announce_resp(tui32 device_id)
|
void devredir_send_server_device_announce_resp(tui32 device_id)
|
||||||
{
|
{
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
int bytes;
|
int bytes;
|
||||||
@ -389,14 +437,14 @@ int dev_redir_send_drive_create_request(tui32 device_id, char *path,
|
|||||||
|
|
||||||
xstream_new(s, 1024 + len);
|
xstream_new(s, 1024 + len);
|
||||||
|
|
||||||
dev_redir_insert_dev_io_req_header(s,
|
devredir_insert_DeviceIoRequest(s,
|
||||||
device_id,
|
device_id,
|
||||||
0,
|
0,
|
||||||
completion_id,
|
completion_id,
|
||||||
IRP_MJ_CREATE,
|
IRP_MJ_CREATE,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
xstream_wr_u32_le(s, DesiredAccess); /* DesiredAccess */
|
xstream_wr_u32_le(s, DesiredAccess); /* DesiredAccess */
|
||||||
xstream_wr_u32_le(s, 0); /* AllocationSize high unused */
|
xstream_wr_u32_le(s, 0); /* AllocationSize high unused */
|
||||||
xstream_wr_u32_le(s, 0); /* AllocationSize low unused */
|
xstream_wr_u32_le(s, 0); /* AllocationSize low unused */
|
||||||
xstream_wr_u32_le(s, 0); /* FileAttributes */
|
xstream_wr_u32_le(s, 0); /* FileAttributes */
|
||||||
@ -404,7 +452,7 @@ int dev_redir_send_drive_create_request(tui32 device_id, char *path,
|
|||||||
xstream_wr_u32_le(s, CreateDisposition); /* CreateDisposition */
|
xstream_wr_u32_le(s, CreateDisposition); /* CreateDisposition */
|
||||||
xstream_wr_u32_le(s, CreateOptions); /* CreateOptions */
|
xstream_wr_u32_le(s, CreateOptions); /* CreateOptions */
|
||||||
xstream_wr_u32_le(s, len); /* PathLength */
|
xstream_wr_u32_le(s, len); /* PathLength */
|
||||||
devredir_cvt_to_unicode(s->p, path); /* path in unicode */
|
devredir_cvt_to_unicode(s->p, path); /* path in unicode */
|
||||||
xstream_seek(s, len);
|
xstream_seek(s, len);
|
||||||
|
|
||||||
/* send to client */
|
/* send to client */
|
||||||
@ -432,8 +480,8 @@ int dev_redir_send_drive_close_request(tui16 Component, tui16 PacketId,
|
|||||||
|
|
||||||
xstream_new(s, 1024);
|
xstream_new(s, 1024);
|
||||||
|
|
||||||
dev_redir_insert_dev_io_req_header(s, DeviceId, FileId, CompletionId,
|
devredir_insert_DeviceIoRequest(s, DeviceId, FileId, CompletionId,
|
||||||
MajorFunction, MinorFunc);
|
MajorFunction, MinorFunc);
|
||||||
|
|
||||||
if (pad_len)
|
if (pad_len)
|
||||||
xstream_seek(s, pad_len);
|
xstream_seek(s, pad_len);
|
||||||
@ -476,12 +524,12 @@ void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id,
|
|||||||
xstream_new(s, 1024 + path_len);
|
xstream_new(s, 1024 + path_len);
|
||||||
|
|
||||||
irp->completion_type = CID_DIRECTORY_CONTROL;
|
irp->completion_type = CID_DIRECTORY_CONTROL;
|
||||||
dev_redir_insert_dev_io_req_header(s,
|
devredir_insert_DeviceIoRequest(s,
|
||||||
device_id,
|
device_id,
|
||||||
irp->FileId,
|
irp->FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_DIRECTORY_CONTROL,
|
IRP_MJ_DIRECTORY_CONTROL,
|
||||||
IRP_MN_QUERY_DIRECTORY);
|
IRP_MN_QUERY_DIRECTORY);
|
||||||
|
|
||||||
#ifdef USE_SHORT_NAMES_IN_DIR_LISTING
|
#ifdef USE_SHORT_NAMES_IN_DIR_LISTING
|
||||||
xstream_wr_u32_le(s, FileBothDirectoryInformation); /* FsInformationClass */
|
xstream_wr_u32_le(s, FileBothDirectoryInformation); /* FsInformationClass */
|
||||||
@ -574,13 +622,14 @@ void dev_redir_proc_client_core_cap_resp(struct stream *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dev_redir_proc_client_devlist_announce_req(struct stream *s)
|
void devredir_proc_client_devlist_announce_req(struct stream *s)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
tui32 device_count;
|
tui32 device_count;
|
||||||
tui32 device_type;
|
tui32 device_type;
|
||||||
tui32 device_data_len;
|
tui32 device_data_len;
|
||||||
|
char preferred_dos_name[9];
|
||||||
|
|
||||||
/* get number of devices being announced */
|
/* get number of devices being announced */
|
||||||
xstream_rd_u32_le(s, device_count);
|
xstream_rd_u32_le(s, device_count);
|
||||||
@ -590,8 +639,7 @@ void dev_redir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
for (i = 0; i < device_count; i++)
|
for (i = 0; i < device_count; i++)
|
||||||
{
|
{
|
||||||
xstream_rd_u32_le(s, device_type);
|
xstream_rd_u32_le(s, device_type);
|
||||||
xstream_rd_u32_le(s, g_device_id); /* LK_TODO need to support */
|
xstream_rd_u32_le(s, g_device_id);
|
||||||
/* multiple drives */
|
|
||||||
|
|
||||||
switch (device_type)
|
switch (device_type)
|
||||||
{
|
{
|
||||||
@ -599,14 +647,11 @@ void dev_redir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
/* get preferred DOS name */
|
/* get preferred DOS name */
|
||||||
for (j = 0; j < 8; j++)
|
for (j = 0; j < 8; j++)
|
||||||
{
|
{
|
||||||
g_preferred_dos_name_for_filesystem[j] = *s->p++;
|
preferred_dos_name[j] = *s->p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DOS names that are 8 chars long are not NULL terminated */
|
/* DOS names that are 8 chars long are not NULL terminated */
|
||||||
g_preferred_dos_name_for_filesystem[8] = 0;
|
preferred_dos_name[8] = 0;
|
||||||
|
|
||||||
/* LK_TODO need to check for invalid chars in DOS name */
|
|
||||||
/* see section 2.2.1.3 of the protocol documentation */
|
|
||||||
|
|
||||||
/* get device data len */
|
/* get device data len */
|
||||||
xstream_rd_u32_le(s, device_data_len);
|
xstream_rd_u32_le(s, device_data_len);
|
||||||
@ -618,22 +663,40 @@ void dev_redir_proc_client_devlist_announce_req(struct stream *s)
|
|||||||
|
|
||||||
log_debug("device_type=FILE_SYSTEM device_id=0x%x dosname=%s "
|
log_debug("device_type=FILE_SYSTEM device_id=0x%x dosname=%s "
|
||||||
"device_data_len=%d full_name=%s", g_device_id,
|
"device_data_len=%d full_name=%s", g_device_id,
|
||||||
g_preferred_dos_name_for_filesystem,
|
preferred_dos_name,
|
||||||
device_data_len, g_full_name_for_filesystem);
|
device_data_len, g_full_name_for_filesystem);
|
||||||
|
|
||||||
dev_redir_send_server_device_announce_resp(g_device_id);
|
devredir_send_server_device_announce_resp(g_device_id);
|
||||||
|
|
||||||
/* create share directory in xrdp file system; */
|
/* create share directory in xrdp file system; */
|
||||||
/* think of this as the mount point for this share */
|
/* think of this as the mount point for this share */
|
||||||
xfuse_create_share(g_device_id,
|
xfuse_create_share(g_device_id, preferred_dos_name);
|
||||||
g_preferred_dos_name_for_filesystem);
|
break;
|
||||||
|
|
||||||
|
case RDPDR_DTYP_SMARTCARD:
|
||||||
|
/* get preferred DOS name */
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
preferred_dos_name[j] = *s->p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DOS names that are 8 chars long are not NULL terminated */
|
||||||
|
preferred_dos_name[8] = 0;
|
||||||
|
|
||||||
|
/* for smart cards, device data len always 0 */
|
||||||
|
|
||||||
|
log_debug("device_type=SMARTCARD device_id=0x%x dosname=%s "
|
||||||
|
"device_data_len=%d",
|
||||||
|
g_device_id, preferred_dos_name, device_data_len);
|
||||||
|
|
||||||
|
devredir_send_server_device_announce_resp(g_device_id);
|
||||||
|
scard_device_announce(g_device_id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* we don't yet support these devices */
|
/* we don't yet support these devices */
|
||||||
case RDPDR_DTYP_SERIAL:
|
case RDPDR_DTYP_SERIAL:
|
||||||
case RDPDR_DTYP_PARALLEL:
|
case RDPDR_DTYP_PARALLEL:
|
||||||
case RDPDR_DTYP_PRINT:
|
case RDPDR_DTYP_PRINT:
|
||||||
case RDPDR_DTYP_SMARTCARD:
|
|
||||||
log_debug("unsupported dev: 0x%x", device_type);
|
log_debug("unsupported dev: 0x%x", device_type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -658,12 +721,19 @@ void dev_redir_proc_device_iocompletion(struct stream *s)
|
|||||||
|
|
||||||
log_debug("entered: IoStatus=0x%x CompletionId=%d", IoStatus, CompletionId);
|
log_debug("entered: IoStatus=0x%x CompletionId=%d", IoStatus, CompletionId);
|
||||||
|
|
||||||
if ((irp = dev_redir_irp_find(CompletionId)) == NULL)
|
if ((irp = devredir_irp_find(CompletionId)) == NULL)
|
||||||
{
|
{
|
||||||
log_error("IRP with completion ID %d not found", CompletionId);
|
log_error("IRP with completion ID %d not found", CompletionId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if callback has been set, call it */
|
||||||
|
if (irp->callback)
|
||||||
|
{
|
||||||
|
(*irp->callback)(s, irp, DeviceId, CompletionId, IoStatus);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
switch (irp->completion_type)
|
switch (irp->completion_type)
|
||||||
{
|
{
|
||||||
case CID_CREATE_DIR_REQ:
|
case CID_CREATE_DIR_REQ:
|
||||||
@ -679,7 +749,7 @@ void dev_redir_proc_device_iocompletion(struct stream *s)
|
|||||||
IoStatus);
|
IoStatus);
|
||||||
free(fuse_data);
|
free(fuse_data);
|
||||||
}
|
}
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -698,7 +768,7 @@ void dev_redir_proc_device_iocompletion(struct stream *s)
|
|||||||
xfuse_devredir_cb_open_file(fuse_data->data_ptr,
|
xfuse_devredir_cb_open_file(fuse_data->data_ptr,
|
||||||
DeviceId, irp->FileId);
|
DeviceId, irp->FileId);
|
||||||
if (irp->type == S_IFDIR)
|
if (irp->type == S_IFDIR)
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CID_READ:
|
case CID_READ:
|
||||||
@ -718,15 +788,15 @@ void dev_redir_proc_device_iocompletion(struct stream *s)
|
|||||||
case CID_CLOSE:
|
case CID_CLOSE:
|
||||||
log_debug("got CID_CLOSE");
|
log_debug("got CID_CLOSE");
|
||||||
log_debug("deleting irp with completion_id=%d comp_type=%d",
|
log_debug("deleting irp with completion_id=%d comp_type=%d",
|
||||||
irp->completion_id, irp->completion_type);
|
irp->CompletionId, irp->completion_type);
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CID_FILE_CLOSE:
|
case CID_FILE_CLOSE:
|
||||||
log_debug("got CID_FILE_CLOSE");
|
log_debug("got CID_FILE_CLOSE");
|
||||||
fuse_data = dev_redir_fuse_data_dequeue(irp);
|
fuse_data = dev_redir_fuse_data_dequeue(irp);
|
||||||
xfuse_devredir_cb_file_close(fuse_data->data_ptr);
|
xfuse_devredir_cb_file_close(fuse_data->data_ptr);
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CID_DIRECTORY_CONTROL:
|
case CID_DIRECTORY_CONTROL:
|
||||||
@ -767,6 +837,8 @@ void dev_redir_proc_device_iocompletion(struct stream *s)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
if (fuse_data)
|
if (fuse_data)
|
||||||
free(fuse_data);
|
free(fuse_data);
|
||||||
|
|
||||||
@ -815,7 +887,7 @@ void dev_redir_proc_query_dir_response(IRP *irp,
|
|||||||
PAKID_CORE_DEVICE_IOREQUEST,
|
PAKID_CORE_DEVICE_IOREQUEST,
|
||||||
DeviceId,
|
DeviceId,
|
||||||
irp->FileId,
|
irp->FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_CLOSE, 0, 32);
|
IRP_MJ_CLOSE, 0, 32);
|
||||||
free(fuse_data);
|
free(fuse_data);
|
||||||
return;
|
return;
|
||||||
@ -908,15 +980,15 @@ int dev_redir_get_dir_listing(void *fusep, tui32 device_id, char *path)
|
|||||||
|
|
||||||
log_debug("fusep=%p", fusep);
|
log_debug("fusep=%p", fusep);
|
||||||
|
|
||||||
if ((irp = dev_redir_irp_new()) == NULL)
|
if ((irp = devredir_irp_new()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* cvt / to windows compatible \ */
|
/* cvt / to windows compatible \ */
|
||||||
devredir_cvt_slash(path);
|
devredir_cvt_slash(path);
|
||||||
|
|
||||||
irp->completion_id = g_completion_id++;
|
irp->CompletionId = g_completion_id++;
|
||||||
irp->completion_type = CID_CREATE_DIR_REQ;
|
irp->completion_type = CID_CREATE_DIR_REQ;
|
||||||
irp->device_id = device_id;
|
irp->DeviceId = device_id;
|
||||||
strcpy(irp->pathname, path);
|
strcpy(irp->pathname, path);
|
||||||
dev_redir_fuse_data_enqueue(irp, fusep);
|
dev_redir_fuse_data_enqueue(irp, fusep);
|
||||||
|
|
||||||
@ -927,7 +999,7 @@ int dev_redir_get_dir_listing(void *fusep, tui32 device_id, char *path)
|
|||||||
rval = dev_redir_send_drive_create_request(device_id, path,
|
rval = dev_redir_send_drive_create_request(device_id, path,
|
||||||
DesiredAccess, CreateOptions,
|
DesiredAccess, CreateOptions,
|
||||||
CreateDisposition,
|
CreateDisposition,
|
||||||
irp->completion_id);
|
irp->CompletionId);
|
||||||
|
|
||||||
log_debug("looking for device_id=%d path=%s", device_id, path);
|
log_debug("looking for device_id=%d path=%s", device_id, path);
|
||||||
|
|
||||||
@ -953,7 +1025,7 @@ int dev_redir_file_open(void *fusep, tui32 device_id, char *path,
|
|||||||
|
|
||||||
log_debug("device_id=%d path=%s mode=0x%x", device_id, path, mode);
|
log_debug("device_id=%d path=%s mode=0x%x", device_id, path, mode);
|
||||||
|
|
||||||
if ((irp = dev_redir_irp_new()) == NULL)
|
if ((irp = devredir_irp_new()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (type & OP_RENAME_FILE)
|
if (type & OP_RENAME_FILE)
|
||||||
@ -966,8 +1038,8 @@ int dev_redir_file_open(void *fusep, tui32 device_id, char *path,
|
|||||||
irp->completion_type = CID_CREATE_OPEN_REQ;
|
irp->completion_type = CID_CREATE_OPEN_REQ;
|
||||||
}
|
}
|
||||||
|
|
||||||
irp->completion_id = g_completion_id++;
|
irp->CompletionId = g_completion_id++;
|
||||||
irp->device_id = device_id;
|
irp->DeviceId = device_id;
|
||||||
strcpy(irp->pathname, path);
|
strcpy(irp->pathname, path);
|
||||||
dev_redir_fuse_data_enqueue(irp, fusep);
|
dev_redir_fuse_data_enqueue(irp, fusep);
|
||||||
|
|
||||||
@ -1009,7 +1081,7 @@ int dev_redir_file_open(void *fusep, tui32 device_id, char *path,
|
|||||||
rval = dev_redir_send_drive_create_request(device_id, path,
|
rval = dev_redir_send_drive_create_request(device_id, path,
|
||||||
DesiredAccess, CreateOptions,
|
DesiredAccess, CreateOptions,
|
||||||
CreateDisposition,
|
CreateDisposition,
|
||||||
irp->completion_id);
|
irp->CompletionId);
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -1021,26 +1093,26 @@ int devredir_file_close(void *fusep, tui32 device_id, tui32 FileId)
|
|||||||
log_debug("entered");
|
log_debug("entered");
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if ((irp = dev_redir_irp_new()) == NULL)
|
if ((irp = devredir_irp_new()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
irp->completion_id = g_completion_id++;
|
irp->CompletionId = g_completion_id++;
|
||||||
#else
|
#else
|
||||||
if ((irp = dev_redir_irp_find_by_fileid(FileId)) == NULL)
|
if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL)
|
||||||
{
|
{
|
||||||
log_error("no IRP found with FileId = %d", FileId);
|
log_error("no IRP found with FileId = %d", FileId);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
irp->completion_type = CID_FILE_CLOSE;
|
irp->completion_type = CID_FILE_CLOSE;
|
||||||
irp->device_id = device_id;
|
irp->DeviceId = device_id;
|
||||||
dev_redir_fuse_data_enqueue(irp, fusep);
|
dev_redir_fuse_data_enqueue(irp, fusep);
|
||||||
|
|
||||||
return dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
|
return dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
|
||||||
PAKID_CORE_DEVICE_IOREQUEST,
|
PAKID_CORE_DEVICE_IOREQUEST,
|
||||||
device_id,
|
device_id,
|
||||||
FileId,
|
FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_CLOSE,
|
IRP_MJ_CLOSE,
|
||||||
0, 32);
|
0, 32);
|
||||||
}
|
}
|
||||||
@ -1057,12 +1129,12 @@ int devredir_rmdir_or_file(void *fusep, tui32 device_id, char *path, int mode)
|
|||||||
int rval;
|
int rval;
|
||||||
IRP *irp;
|
IRP *irp;
|
||||||
|
|
||||||
if ((irp = dev_redir_irp_new()) == NULL)
|
if ((irp = devredir_irp_new()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
irp->completion_id = g_completion_id++;
|
irp->CompletionId = g_completion_id++;
|
||||||
irp->completion_type = CID_RMDIR_OR_FILE;
|
irp->completion_type = CID_RMDIR_OR_FILE;
|
||||||
irp->device_id = device_id;
|
irp->DeviceId = device_id;
|
||||||
strcpy(irp->pathname, path);
|
strcpy(irp->pathname, path);
|
||||||
dev_redir_fuse_data_enqueue(irp, fusep);
|
dev_redir_fuse_data_enqueue(irp, fusep);
|
||||||
|
|
||||||
@ -1079,7 +1151,7 @@ int devredir_rmdir_or_file(void *fusep, tui32 device_id, char *path, int mode)
|
|||||||
rval = dev_redir_send_drive_create_request(device_id, path,
|
rval = dev_redir_send_drive_create_request(device_id, path,
|
||||||
DesiredAccess, CreateOptions,
|
DesiredAccess, CreateOptions,
|
||||||
CreateDisposition,
|
CreateDisposition,
|
||||||
irp->completion_id);
|
irp->CompletionId);
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -1099,7 +1171,7 @@ int dev_redir_file_read(void *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
|
|
||||||
xstream_new(s, 1024);
|
xstream_new(s, 1024);
|
||||||
|
|
||||||
if ((irp = dev_redir_irp_find_by_fileid(FileId)) == NULL)
|
if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL)
|
||||||
{
|
{
|
||||||
log_error("no IRP found with FileId = %d", FileId);
|
log_error("no IRP found with FileId = %d", FileId);
|
||||||
xfuse_devredir_cb_read_file(fusep, NULL, 0);
|
xfuse_devredir_cb_read_file(fusep, NULL, 0);
|
||||||
@ -1108,12 +1180,12 @@ int dev_redir_file_read(void *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
|
|
||||||
irp->completion_type = CID_READ;
|
irp->completion_type = CID_READ;
|
||||||
dev_redir_fuse_data_enqueue(irp, fusep);
|
dev_redir_fuse_data_enqueue(irp, fusep);
|
||||||
dev_redir_insert_dev_io_req_header(s,
|
devredir_insert_DeviceIoRequest(s,
|
||||||
DeviceId,
|
DeviceId,
|
||||||
FileId,
|
FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_READ,
|
IRP_MJ_READ,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
xstream_wr_u32_le(s, Length);
|
xstream_wr_u32_le(s, Length);
|
||||||
xstream_wr_u64_le(s, Offset);
|
xstream_wr_u64_le(s, Offset);
|
||||||
@ -1139,7 +1211,7 @@ int dev_redir_file_write(void *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
|
|
||||||
xstream_new(s, 1024 + Length);
|
xstream_new(s, 1024 + Length);
|
||||||
|
|
||||||
if ((irp = dev_redir_irp_find_by_fileid(FileId)) == NULL)
|
if ((irp = devredir_irp_find_by_fileid(FileId)) == NULL)
|
||||||
{
|
{
|
||||||
log_error("no IRP found with FileId = %d", FileId);
|
log_error("no IRP found with FileId = %d", FileId);
|
||||||
xfuse_devredir_cb_write_file(fusep, NULL, 0);
|
xfuse_devredir_cb_write_file(fusep, NULL, 0);
|
||||||
@ -1148,12 +1220,12 @@ int dev_redir_file_write(void *fusep, tui32 DeviceId, tui32 FileId,
|
|||||||
|
|
||||||
irp->completion_type = CID_WRITE;
|
irp->completion_type = CID_WRITE;
|
||||||
dev_redir_fuse_data_enqueue(irp, fusep);
|
dev_redir_fuse_data_enqueue(irp, fusep);
|
||||||
dev_redir_insert_dev_io_req_header(s,
|
devredir_insert_DeviceIoRequest(s,
|
||||||
DeviceId,
|
DeviceId,
|
||||||
FileId,
|
FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_WRITE,
|
IRP_MJ_WRITE,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
xstream_wr_u32_le(s, Length);
|
xstream_wr_u32_le(s, Length);
|
||||||
xstream_wr_u64_le(s, Offset);
|
xstream_wr_u64_le(s, Offset);
|
||||||
@ -1249,187 +1321,16 @@ int dev_redir_fuse_data_enqueue(IRP *irp, void *vp)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
** IRP stuff **
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new IRP and append to linked list
|
|
||||||
*
|
|
||||||
* @return new IRP or NULL on error
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
IRP * dev_redir_irp_new()
|
|
||||||
{
|
|
||||||
IRP *irp;
|
|
||||||
IRP *irp_last;
|
|
||||||
|
|
||||||
log_debug("=== entered");
|
|
||||||
|
|
||||||
/* create new IRP */
|
|
||||||
if ((irp = calloc(1, sizeof(IRP))) == NULL)
|
|
||||||
{
|
|
||||||
log_error("system out of memory!");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* insert at end of linked list */
|
|
||||||
if ((irp_last = dev_redir_irp_get_last()) == NULL)
|
|
||||||
{
|
|
||||||
/* list is empty, this is the first entry */
|
|
||||||
g_irp_head = irp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
irp_last->next = irp;
|
|
||||||
irp->prev = irp_last;
|
|
||||||
}
|
|
||||||
|
|
||||||
return irp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete specified IRP from linked list
|
|
||||||
*
|
|
||||||
* @return 0 on success, -1 on failure
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
int dev_redir_irp_delete(IRP *irp)
|
|
||||||
{
|
|
||||||
IRP *lirp = g_irp_head;
|
|
||||||
|
|
||||||
log_debug("=== entered; completion_id=%d type=%d",
|
|
||||||
irp->completion_id, irp->completion_type);
|
|
||||||
|
|
||||||
if ((irp == NULL) || (lirp == NULL))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
dev_redir_irp_dump(); // LK_TODO
|
|
||||||
|
|
||||||
while (lirp)
|
|
||||||
{
|
|
||||||
if (lirp == irp)
|
|
||||||
break;
|
|
||||||
|
|
||||||
lirp = lirp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lirp == NULL)
|
|
||||||
return -1; /* did not find specified irp */
|
|
||||||
|
|
||||||
if (lirp->prev == NULL)
|
|
||||||
{
|
|
||||||
/* we are at head of linked list */
|
|
||||||
if (lirp->next == NULL)
|
|
||||||
{
|
|
||||||
/* only one element in list */
|
|
||||||
free(lirp);
|
|
||||||
g_irp_head = NULL;
|
|
||||||
dev_redir_irp_dump(); // LK_TODO
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lirp->next->prev = NULL;
|
|
||||||
g_irp_head = lirp->next;
|
|
||||||
free(lirp);
|
|
||||||
}
|
|
||||||
else if (lirp->next == NULL)
|
|
||||||
{
|
|
||||||
/* we are at tail of linked list */
|
|
||||||
lirp->prev->next = NULL;
|
|
||||||
free(lirp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* we are in between */
|
|
||||||
lirp->prev->next = lirp->next;
|
|
||||||
lirp->next->prev = lirp->prev;
|
|
||||||
free(lirp);
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_redir_irp_dump(); // LK_TODO
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return IRP containing specified completion_id
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
IRP *dev_redir_irp_find(tui32 completion_id)
|
|
||||||
{
|
|
||||||
IRP *irp = g_irp_head;
|
|
||||||
|
|
||||||
while (irp)
|
|
||||||
{
|
|
||||||
if (irp->completion_id == completion_id)
|
|
||||||
return irp;
|
|
||||||
|
|
||||||
irp = irp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
IRP * dev_redir_irp_find_by_fileid(tui32 FileId)
|
|
||||||
{
|
|
||||||
IRP *irp = g_irp_head;
|
|
||||||
|
|
||||||
while (irp)
|
|
||||||
{
|
|
||||||
if (irp->FileId == FileId)
|
|
||||||
return irp;
|
|
||||||
|
|
||||||
irp = irp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return last IRP in linked list
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
IRP * dev_redir_irp_get_last()
|
|
||||||
{
|
|
||||||
IRP *irp = g_irp_head;
|
|
||||||
|
|
||||||
while (irp)
|
|
||||||
{
|
|
||||||
if (irp->next == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
irp = irp->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return irp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dev_redir_irp_dump()
|
|
||||||
{
|
|
||||||
IRP *irp = g_irp_head;
|
|
||||||
|
|
||||||
log_debug("------- dumping IRPs --------");
|
|
||||||
while (irp)
|
|
||||||
{
|
|
||||||
log_debug(" completion_id=%d\tcompletion_type=%d\tFileId=%d",
|
|
||||||
irp->completion_id, irp->completion_type, irp->FileId);
|
|
||||||
|
|
||||||
irp = irp->next;
|
|
||||||
}
|
|
||||||
log_debug("------- dumping IRPs done ---");
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
** miscellaneous stuff **
|
** miscellaneous stuff **
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
void dev_redir_insert_dev_io_req_header(struct stream *s,
|
void devredir_insert_DeviceIoRequest(struct stream *s,
|
||||||
tui32 DeviceId,
|
tui32 DeviceId,
|
||||||
tui32 FileId,
|
tui32 FileId,
|
||||||
tui32 CompletionId,
|
tui32 CompletionId,
|
||||||
tui32 MajorFunction,
|
tui32 MajorFunction,
|
||||||
tui32 MinorFunction)
|
tui32 MinorFunction)
|
||||||
{
|
{
|
||||||
/* setup DR_DEVICE_IOREQUEST header */
|
/* setup DR_DEVICE_IOREQUEST header */
|
||||||
xstream_wr_u16_le(s, RDPDR_CTYP_CORE);
|
xstream_wr_u16_le(s, RDPDR_CTYP_CORE);
|
||||||
@ -1492,7 +1393,7 @@ int dev_redir_string_ends_with(char *string, char c)
|
|||||||
return (string[len - 1] == c) ? 1 : 0;
|
return (string[len - 1] == c) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dev_redir_insert_rdpdr_header(struct stream *s, tui16 Component,
|
void devredir_insert_RDPDR_header(struct stream *s, tui16 Component,
|
||||||
tui16 PacketId)
|
tui16 PacketId)
|
||||||
{
|
{
|
||||||
xstream_wr_u16_le(s, Component);
|
xstream_wr_u16_le(s, Component);
|
||||||
@ -1512,16 +1413,16 @@ void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus)
|
|||||||
xfuse_devredir_cb_rmdir_or_file(fuse_data->data_ptr, IoStatus);
|
xfuse_devredir_cb_rmdir_or_file(fuse_data->data_ptr, IoStatus);
|
||||||
free(fuse_data);
|
free(fuse_data);
|
||||||
}
|
}
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xstream_new(s, 1024);
|
xstream_new(s, 1024);
|
||||||
|
|
||||||
irp->completion_type = CID_RMDIR_OR_FILE_RESP;
|
irp->completion_type = CID_RMDIR_OR_FILE_RESP;
|
||||||
dev_redir_insert_dev_io_req_header(s, irp->device_id, irp->FileId,
|
devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_SET_INFORMATION, 0);
|
IRP_MJ_SET_INFORMATION, 0);
|
||||||
|
|
||||||
xstream_wr_u32_le(s, FileDispositionInformation);
|
xstream_wr_u32_le(s, FileDispositionInformation);
|
||||||
xstream_wr_u32_le(s, 0); /* length is zero */
|
xstream_wr_u32_le(s, 0); /* length is zero */
|
||||||
@ -1548,16 +1449,16 @@ void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus)
|
|||||||
|
|
||||||
if (IoStatus != NT_STATUS_SUCCESS)
|
if (IoStatus != NT_STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
irp->completion_type = CID_CLOSE;
|
irp->completion_type = CID_CLOSE;
|
||||||
dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
|
dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
|
||||||
PAKID_CORE_DEVICE_IOREQUEST,
|
PAKID_CORE_DEVICE_IOREQUEST,
|
||||||
irp->device_id,
|
irp->DeviceId,
|
||||||
irp->FileId,
|
irp->FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_CLOSE, 0, 32);
|
IRP_MJ_CLOSE, 0, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1579,16 +1480,16 @@ void devredir_proc_cid_rename_file(IRP *irp, tui32 IoStatus)
|
|||||||
xfuse_devredir_cb_rename_file(fuse_data->data_ptr, IoStatus);
|
xfuse_devredir_cb_rename_file(fuse_data->data_ptr, IoStatus);
|
||||||
free(fuse_data);
|
free(fuse_data);
|
||||||
}
|
}
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xstream_new(s, 1024);
|
xstream_new(s, 1024);
|
||||||
|
|
||||||
irp->completion_type = CID_RENAME_FILE_RESP;
|
irp->completion_type = CID_RENAME_FILE_RESP;
|
||||||
dev_redir_insert_dev_io_req_header(s, irp->device_id, irp->FileId,
|
devredir_insert_DeviceIoRequest(s, irp->DeviceId, irp->FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_SET_INFORMATION, 0);
|
IRP_MJ_SET_INFORMATION, 0);
|
||||||
|
|
||||||
flen = strlen(irp->gen_buf) * 2 + 2;
|
flen = strlen(irp->gen_buf) * 2 + 2;
|
||||||
sblen = 6 + flen;
|
sblen = 6 + flen;
|
||||||
@ -1627,15 +1528,15 @@ void devredir_proc_cid_rename_file_resp(IRP *irp, tui32 IoStatus)
|
|||||||
|
|
||||||
if (IoStatus != NT_STATUS_SUCCESS)
|
if (IoStatus != NT_STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
dev_redir_irp_delete(irp);
|
devredir_irp_delete(irp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
irp->completion_type = CID_CLOSE;
|
irp->completion_type = CID_CLOSE;
|
||||||
dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
|
dev_redir_send_drive_close_request(RDPDR_CTYP_CORE,
|
||||||
PAKID_CORE_DEVICE_IOREQUEST,
|
PAKID_CORE_DEVICE_IOREQUEST,
|
||||||
irp->device_id,
|
irp->DeviceId,
|
||||||
irp->FileId,
|
irp->FileId,
|
||||||
irp->completion_id,
|
irp->CompletionId,
|
||||||
IRP_MJ_CLOSE, 0, 32);
|
IRP_MJ_CLOSE, 0, 32);
|
||||||
}
|
}
|
||||||
|
@ -23,59 +23,14 @@
|
|||||||
#if !defined(DEVREDIR_H)
|
#if !defined(DEVREDIR_H)
|
||||||
#define DEVREDIR_H
|
#define DEVREDIR_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include "irp.h"
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "arch.h"
|
|
||||||
#include "parse.h"
|
|
||||||
#include "os_calls.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "chansrv_fuse.h"
|
|
||||||
|
|
||||||
#define USE_SHORT_NAMES_IN_DIR_LISTING
|
#define USE_SHORT_NAMES_IN_DIR_LISTING
|
||||||
|
|
||||||
typedef struct fuse_data FUSE_DATA;
|
|
||||||
struct fuse_data
|
|
||||||
{
|
|
||||||
void *data_ptr;
|
|
||||||
FUSE_DATA *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* An I/O Resource Packet to track dev_dir I/O calls */
|
|
||||||
|
|
||||||
typedef struct irp IRP;
|
|
||||||
|
|
||||||
struct irp
|
|
||||||
{
|
|
||||||
tui32 completion_id; /* unique number */
|
|
||||||
char completion_type; /* describes I/O type */
|
|
||||||
tui32 FileId; /* RDP client provided unique number */
|
|
||||||
char pathname[256]; /* absolute pathname */
|
|
||||||
char gen_buf[1024]; /* for general use */
|
|
||||||
int type;
|
|
||||||
tui32 device_id; /* identifies remote device */
|
|
||||||
FUSE_DATA *fd_head; /* point to first FUSE opaque object */
|
|
||||||
FUSE_DATA *fd_tail; /* point to last FUSE opaque object */
|
|
||||||
IRP *next; /* point to next IRP */
|
|
||||||
IRP *prev; /* point to previous IRP */
|
|
||||||
};
|
|
||||||
|
|
||||||
void *dev_redir_fuse_data_peek(IRP *irp);
|
void *dev_redir_fuse_data_peek(IRP *irp);
|
||||||
void *dev_redir_fuse_data_dequeue(IRP *irp);
|
void *dev_redir_fuse_data_dequeue(IRP *irp);
|
||||||
int dev_redir_fuse_data_enqueue(IRP *irp, void *vp);
|
int dev_redir_fuse_data_enqueue(IRP *irp, void *vp);
|
||||||
|
|
||||||
IRP * dev_redir_irp_new();
|
|
||||||
IRP * dev_redir_irp_find(tui32 completion_id);
|
|
||||||
IRP * dev_redir_irp_find_by_fileid(tui32 FileId);
|
|
||||||
IRP * dev_redir_irp_get_last();
|
|
||||||
int dev_redir_irp_delete(IRP *irp);
|
|
||||||
void dev_redir_irp_dump();
|
|
||||||
|
|
||||||
int APP_CC dev_redir_init(void);
|
int APP_CC dev_redir_init(void);
|
||||||
int APP_CC dev_redir_deinit(void);
|
int APP_CC dev_redir_deinit(void);
|
||||||
|
|
||||||
@ -88,7 +43,7 @@ int APP_CC dev_redir_check_wait_objs(void);
|
|||||||
void dev_redir_send_server_core_cap_req();
|
void dev_redir_send_server_core_cap_req();
|
||||||
void dev_redir_send_server_clientID_confirm();
|
void dev_redir_send_server_clientID_confirm();
|
||||||
void dev_redir_send_server_user_logged_on();
|
void dev_redir_send_server_user_logged_on();
|
||||||
void dev_redir_send_server_device_announce_resp(tui32 device_id);
|
void devredir_send_server_device_announce_resp(tui32 device_id);
|
||||||
|
|
||||||
void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id,
|
void dev_redir_send_drive_dir_request(IRP *irp, tui32 device_id,
|
||||||
tui32 InitialQuery, char *Path);
|
tui32 InitialQuery, char *Path);
|
||||||
@ -107,7 +62,7 @@ int dev_redir_send_drive_close_request(tui16 Component, tui16 PacketId,
|
|||||||
tui32 MinorFunc,
|
tui32 MinorFunc,
|
||||||
int pad_len);
|
int pad_len);
|
||||||
|
|
||||||
void dev_redir_proc_client_devlist_announce_req(struct stream *s);
|
void devredir_proc_client_devlist_announce_req(struct stream *s);
|
||||||
void dev_redir_proc_client_core_cap_resp(struct stream *s);
|
void dev_redir_proc_client_core_cap_resp(struct stream *s);
|
||||||
void dev_redir_proc_device_iocompletion(struct stream *s);
|
void dev_redir_proc_device_iocompletion(struct stream *s);
|
||||||
|
|
||||||
@ -118,20 +73,20 @@ void dev_redir_proc_query_dir_response(IRP *irp,
|
|||||||
tui32 IoStatus);
|
tui32 IoStatus);
|
||||||
|
|
||||||
/* misc stuff */
|
/* misc stuff */
|
||||||
void dev_redir_insert_dev_io_req_header(struct stream *s,
|
void devredir_insert_DeviceIoRequest(struct stream *s,
|
||||||
tui32 DeviceId,
|
tui32 DeviceId,
|
||||||
tui32 FileId,
|
tui32 FileId,
|
||||||
tui32 CompletionId,
|
tui32 CompletionId,
|
||||||
tui32 MajorFunction,
|
tui32 MajorFunction,
|
||||||
tui32 MinorFunction);
|
tui32 MinorFunction);
|
||||||
|
|
||||||
void devredir_cvt_slash(char *path);
|
void devredir_cvt_slash(char *path);
|
||||||
void devredir_cvt_to_unicode(char *unicode, char *path);
|
void devredir_cvt_to_unicode(char *unicode, char *path);
|
||||||
void devredir_cvt_from_unicode_len(char *path, char *unicode, int len);
|
void devredir_cvt_from_unicode_len(char *path, char *unicode, int len);
|
||||||
int dev_redir_string_ends_with(char *string, char c);
|
int dev_redir_string_ends_with(char *string, char c);
|
||||||
|
|
||||||
void dev_redir_insert_rdpdr_header(struct stream *s, tui16 Component,
|
void devredir_insert_RDPDR_header(struct stream *s, tui16 Component,
|
||||||
tui16 PacketId);
|
tui16 PacketId);
|
||||||
|
|
||||||
void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus);
|
void devredir_proc_cid_rmdir_or_file(IRP *irp, tui32 IoStatus);
|
||||||
void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus);
|
void devredir_proc_cid_rmdir_or_file_resp(IRP *irp, tui32 IoStatus);
|
||||||
@ -149,42 +104,6 @@ int devredir_file_close(void *fusep, tui32 device_id, tui32 file_id);
|
|||||||
int dev_redir_file_read(void *fusep, tui32 device_id, tui32 FileId,
|
int dev_redir_file_read(void *fusep, tui32 device_id, tui32 FileId,
|
||||||
tui32 Length, tui64 Offset);
|
tui32 Length, tui64 Offset);
|
||||||
|
|
||||||
/* module based logging */
|
|
||||||
#define LOG_ERROR 0
|
|
||||||
#define LOG_INFO 1
|
|
||||||
#define LOG_DEBUG 2
|
|
||||||
|
|
||||||
#ifndef LOG_LEVEL
|
|
||||||
#define LOG_LEVEL LOG_ERROR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define log_error(_params...) \
|
|
||||||
{ \
|
|
||||||
g_write("[%10.10u]: DEV_REDIR %s: %d : ERROR: ", \
|
|
||||||
g_time3(), __func__, __LINE__); \
|
|
||||||
g_writeln (_params); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define log_info(_params...) \
|
|
||||||
{ \
|
|
||||||
if (LOG_INFO <= LOG_LEVEL) \
|
|
||||||
{ \
|
|
||||||
g_write("[%10.10u]: DEV_REDIR %s: %d : ", \
|
|
||||||
g_time3(), __func__, __LINE__); \
|
|
||||||
g_writeln (_params); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define log_debug(_params...) \
|
|
||||||
{ \
|
|
||||||
if (LOG_DEBUG <= LOG_LEVEL) \
|
|
||||||
{ \
|
|
||||||
g_write("[%10.10u]: DEV_REDIR %s: %d : ", \
|
|
||||||
g_time3(), __func__, __LINE__); \
|
|
||||||
g_writeln (_params); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
int send_channel_data(int chan_id, char *data, int size);
|
int send_channel_data(int chan_id, char *data, int size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
231
sesman/chansrv/irp.c
Normal file
231
sesman/chansrv/irp.c
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/**
|
||||||
|
* xrdp: A Remote Desktop Protocol server.
|
||||||
|
*
|
||||||
|
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* manage I/O for redirected file system and devices
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
#include "os_calls.h"
|
||||||
|
#include "irp.h"
|
||||||
|
|
||||||
|
/* module based logging */
|
||||||
|
#define LOG_ERROR 0
|
||||||
|
#define LOG_INFO 1
|
||||||
|
#define LOG_DEBUG 2
|
||||||
|
|
||||||
|
#ifndef LOG_LEVEL
|
||||||
|
#define LOG_LEVEL LOG_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define log_error(_params...) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: IRP %s: %d : ERROR: ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define log_info(_params...) \
|
||||||
|
{ \
|
||||||
|
if (LOG_INFO <= LOG_LEVEL) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: IRP %s: %d : ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define log_debug(_params...) \
|
||||||
|
{ \
|
||||||
|
if (LOG_DEBUG <= LOG_LEVEL) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: IRP %s: %d : ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
IRP *g_irp_head = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new IRP and append to linked list
|
||||||
|
*
|
||||||
|
* @return new IRP or NULL on error
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
IRP * devredir_irp_new()
|
||||||
|
{
|
||||||
|
IRP *irp;
|
||||||
|
IRP *irp_last;
|
||||||
|
|
||||||
|
log_debug("entered");
|
||||||
|
|
||||||
|
/* create new IRP */
|
||||||
|
if ((irp = g_malloc(sizeof(IRP), 1)) == NULL)
|
||||||
|
{
|
||||||
|
log_error("system out of memory!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* insert at end of linked list */
|
||||||
|
if ((irp_last = devredir_irp_get_last()) == NULL)
|
||||||
|
{
|
||||||
|
/* list is empty, this is the first entry */
|
||||||
|
g_irp_head = irp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
irp_last->next = irp;
|
||||||
|
irp->prev = irp_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return irp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete specified IRP from linked list
|
||||||
|
*
|
||||||
|
* @return 0 on success, -1 on failure
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
int devredir_irp_delete(IRP *irp)
|
||||||
|
{
|
||||||
|
IRP *lirp = g_irp_head;
|
||||||
|
|
||||||
|
log_debug("=== entered; completion_id=%d type=%d",
|
||||||
|
irp->CompletionId, irp->completion_type);
|
||||||
|
|
||||||
|
if ((irp == NULL) || (lirp == NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
devredir_irp_dump(); // LK_TODO
|
||||||
|
|
||||||
|
while (lirp)
|
||||||
|
{
|
||||||
|
if (lirp == irp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
lirp = lirp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lirp == NULL)
|
||||||
|
return -1; /* did not find specified irp */
|
||||||
|
|
||||||
|
if (lirp->prev == NULL)
|
||||||
|
{
|
||||||
|
/* we are at head of linked list */
|
||||||
|
if (lirp->next == NULL)
|
||||||
|
{
|
||||||
|
/* only one element in list */
|
||||||
|
g_free(lirp);
|
||||||
|
g_irp_head = NULL;
|
||||||
|
devredir_irp_dump(); // LK_TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lirp->next->prev = NULL;
|
||||||
|
g_irp_head = lirp->next;
|
||||||
|
g_free(lirp);
|
||||||
|
}
|
||||||
|
else if (lirp->next == NULL)
|
||||||
|
{
|
||||||
|
/* we are at tail of linked list */
|
||||||
|
lirp->prev->next = NULL;
|
||||||
|
g_free(lirp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* we are in between */
|
||||||
|
lirp->prev->next = lirp->next;
|
||||||
|
lirp->next->prev = lirp->prev;
|
||||||
|
g_free(lirp);
|
||||||
|
}
|
||||||
|
|
||||||
|
devredir_irp_dump(); // LK_TODO
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return IRP containing specified completion_id
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
IRP *devredir_irp_find(tui32 completion_id)
|
||||||
|
{
|
||||||
|
IRP *irp = g_irp_head;
|
||||||
|
|
||||||
|
while (irp)
|
||||||
|
{
|
||||||
|
if (irp->CompletionId == completion_id)
|
||||||
|
return irp;
|
||||||
|
|
||||||
|
irp = irp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRP * devredir_irp_find_by_fileid(tui32 FileId)
|
||||||
|
{
|
||||||
|
IRP *irp = g_irp_head;
|
||||||
|
|
||||||
|
while (irp)
|
||||||
|
{
|
||||||
|
if (irp->FileId == FileId)
|
||||||
|
return irp;
|
||||||
|
|
||||||
|
irp = irp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return last IRP in linked list
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
IRP * devredir_irp_get_last()
|
||||||
|
{
|
||||||
|
IRP *irp = g_irp_head;
|
||||||
|
|
||||||
|
while (irp)
|
||||||
|
{
|
||||||
|
if (irp->next == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
irp = irp->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return irp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void devredir_irp_dump()
|
||||||
|
{
|
||||||
|
IRP *irp = g_irp_head;
|
||||||
|
|
||||||
|
log_debug("------- dumping IRPs --------");
|
||||||
|
while (irp)
|
||||||
|
{
|
||||||
|
log_debug(" completion_id=%d\tcompletion_type=%d\tFileId=%d",
|
||||||
|
irp->CompletionId, irp->completion_type, irp->FileId);
|
||||||
|
|
||||||
|
irp = irp->next;
|
||||||
|
}
|
||||||
|
log_debug("------- dumping IRPs done ---");
|
||||||
|
}
|
64
sesman/chansrv/irp.h
Normal file
64
sesman/chansrv/irp.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* xrdp: A Remote Desktop Protocol server.
|
||||||
|
*
|
||||||
|
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* manage I/O for redirected file system and devices
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IRP_H
|
||||||
|
#define __IRP_H
|
||||||
|
|
||||||
|
typedef struct fuse_data FUSE_DATA;
|
||||||
|
struct fuse_data
|
||||||
|
{
|
||||||
|
void *data_ptr;
|
||||||
|
FUSE_DATA *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* An I/O Resource Packet to track I/O calls */
|
||||||
|
|
||||||
|
typedef struct irp IRP;
|
||||||
|
|
||||||
|
struct irp
|
||||||
|
{
|
||||||
|
tui32 CompletionId; /* unique number */
|
||||||
|
tui32 DeviceId; /* identifies remote device */
|
||||||
|
tui32 FileId; /* RDP client provided unique number */
|
||||||
|
char completion_type; /* describes I/O type */
|
||||||
|
char pathname[256]; /* absolute pathname */
|
||||||
|
char gen_buf[1024]; /* for general use */
|
||||||
|
int type;
|
||||||
|
FUSE_DATA *fd_head; /* point to first FUSE opaque object */
|
||||||
|
FUSE_DATA *fd_tail; /* point to last FUSE opaque object */
|
||||||
|
IRP *next; /* point to next IRP */
|
||||||
|
IRP *prev; /* point to previous IRP */
|
||||||
|
int scard_index; /* used to smart card to locate dev */
|
||||||
|
|
||||||
|
void (*callback)(struct stream *s, IRP *irp, tui32 DeviceId,
|
||||||
|
tui32 CompletionId, tui32 IoStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
IRP * devredir_irp_new();
|
||||||
|
int devredir_irp_delete(IRP *irp);
|
||||||
|
IRP * devredir_irp_find(tui32 completion_id);
|
||||||
|
IRP * devredir_irp_find_by_fileid(tui32 FileId);
|
||||||
|
IRP * devredir_irp_get_last();
|
||||||
|
void devredir_irp_dump();
|
||||||
|
|
||||||
|
#endif /* end ifndef __IRP_H */
|
528
sesman/chansrv/smartcard.c
Normal file
528
sesman/chansrv/smartcard.c
Normal file
@ -0,0 +1,528 @@
|
|||||||
|
/**
|
||||||
|
* xrdp: A Remote Desktop Protocol server.
|
||||||
|
*
|
||||||
|
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* smartcard redirection support
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "os_calls.h"
|
||||||
|
#include "smartcard.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "irp.h"
|
||||||
|
#include "devredir.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* o need to query client for build number and determine whether we should use
|
||||||
|
* SCREDIR_VERSION_XP or SCREDIR_VERSION_LONGHORN
|
||||||
|
*
|
||||||
|
* o need to call scard_release_resources()
|
||||||
|
*
|
||||||
|
* o why is win 7 sending SCARD_IOCTL_ACCESS_STARTED_EVENT first
|
||||||
|
* 0000 00 01 00 00 04 00 00 00 e0 00 09 00 00 00 00 00 ................
|
||||||
|
* 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
* 0020 28 b7 9d 02
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notes:
|
||||||
|
*
|
||||||
|
* XP and Server 2003 use version SCREDIR_VERSION_XP functions 5 - 58
|
||||||
|
* Vista and Server 2008 use version SCREDIR_VERSION_LONGHORN functions 5 - 64
|
||||||
|
* if TS Client's build number is >= 4,034 use SCREDIR_VERSION_LONGHORN
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* module based logging */
|
||||||
|
#define LOG_ERROR 0
|
||||||
|
#define LOG_INFO 1
|
||||||
|
#define LOG_DEBUG 2
|
||||||
|
|
||||||
|
#ifndef LOG_LEVEL
|
||||||
|
#define LOG_LEVEL LOG_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define log_error(_params...) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: SMART_CARD %s: %d : ERROR: ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define log_info(_params...) \
|
||||||
|
{ \
|
||||||
|
if (LOG_INFO <= LOG_LEVEL) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: SMART_CARD %s: %d : ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define log_debug(_params...) \
|
||||||
|
{ \
|
||||||
|
if (LOG_DEBUG <= LOG_LEVEL) \
|
||||||
|
{ \
|
||||||
|
g_write("[%10.10u]: SMART_CARD %s: %d : ", \
|
||||||
|
g_time3(), __func__, __LINE__); \
|
||||||
|
g_writeln (_params); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [MS-RDPESC] 3.1.4 */
|
||||||
|
#define SCARD_IOCTL_ESTABLISH_CONTEXT 0x00090014 /* EstablishContext */
|
||||||
|
#define SCARD_IOCTL_RELEASE_CONTEXT 0x00090018 /* ReleaseContext */
|
||||||
|
#define SCARD_IOCTL_IS_VALID_CONTEXT 0x0009001C /* IsValidContext */
|
||||||
|
#define SCARD_IOCTL_LIST_READER_GROUPS 0x00090020 /* ListReaderGroups */
|
||||||
|
#define SCARD_IOCTL_LIST_READERS_A 0x00090028 /* ListReaders ASCII */
|
||||||
|
#define SCARD_IOCTL_LIST_READERS_W 0x0009002C /* ListReaders Wide */
|
||||||
|
#define SCARD_IOCTL_INTRODUCE_READER_GROUP 0x00090050 /* IntroduceReaderGroup */
|
||||||
|
#define SCARD_IOCTL_FORGET_READER_GROUP 0x00090058 /* ForgetReader */
|
||||||
|
#define SCARD_IOCTL_INTRODUCE_READER 0x00090060 /* IntroduceReader */
|
||||||
|
#define SCARD_IOCTL_FORGET_READER 0x00090068 /* IntroduceReader */
|
||||||
|
#define SCARD_IOCTL_ADD_READER_TO_GROUP 0x00090070 /* AddReaderToGroup */
|
||||||
|
#define SCARD_IOCTL_REMOVE_READER_FROM_GROUP 0x00090078 /* RemoveReaderFromGroup */
|
||||||
|
#define SCARD_IOCTL_GET_STATUS_CHANGE 0x000900A0 /* GetStatusChangeA */
|
||||||
|
#define SCARD_IOCTL_CANCEL 0x000900A8 /* Cancel */
|
||||||
|
#define SCARD_IOCTL_CONNECT 0x000900AC /* ConnectA */
|
||||||
|
#define SCARD_IOCTL_RECONNECT 0x000900B4 /* Reconnect */
|
||||||
|
#define SCARD_IOCTL_DISCONNECT 0x000900B8 /* Disconnect */
|
||||||
|
#define SCARD_IOCTL_BEGIN_TRANSACTION 0x000900BC /* BeginTransaction */
|
||||||
|
#define SCARD_IOCTL_END_TRANSACTION 0x000900C0 /* EndTransaction */
|
||||||
|
#define SCARD_IOCTL_STATE 0x000900C4 /* State */
|
||||||
|
#define SCARD_IOCTL_STATUS 0x000900C8 /* StatusA */
|
||||||
|
#define SCARD_IOCTL_TRANSMIT 0x000900D0 /* Transmit */
|
||||||
|
#define SCARD_IOCTL_CONTROL 0x000900D4 /* Control */
|
||||||
|
#define SCARD_IOCTL_GETATTRIB 0x000900D8 /* GetAttrib */
|
||||||
|
#define SCARD_IOCTL_SETATTRIB 0x000900DC /* SetAttrib */
|
||||||
|
#define SCARD_IOCTL_ACCESS_STARTED_EVENT 0x000900E0 /* SCardAccessStartedEvent */
|
||||||
|
#define SCARD_IOCTL_LOCATE_CARDS_BY_ATR 0x000900E8 /* LocateCardsByATR */
|
||||||
|
|
||||||
|
/* scope used in EstablishContextCall */
|
||||||
|
#define SCARD_SCOPE_USER 0x00000000
|
||||||
|
#define SCARD_SCOPE_TERMINAL 0x00000001
|
||||||
|
#define SCARD_SCOPE_SYSTEM 0x00000002
|
||||||
|
|
||||||
|
#define MAX_SMARTCARDS 16
|
||||||
|
|
||||||
|
/* stores info about a smart card */
|
||||||
|
typedef struct smartcard
|
||||||
|
{
|
||||||
|
tui32 DeviceId;
|
||||||
|
char Context[16]; /* opaque context; save as passed to us */
|
||||||
|
int Context_len; /* Context len in bytes */
|
||||||
|
} SMARTCARD;
|
||||||
|
|
||||||
|
SMARTCARD *smartcards[MAX_SMARTCARDS];
|
||||||
|
int g_smartcards_inited = 0;
|
||||||
|
|
||||||
|
extern tui32 g_completion_id;
|
||||||
|
extern int g_rdpdr_chan_id; /* in chansrv.c */
|
||||||
|
|
||||||
|
/* forward declarations specific to this file */
|
||||||
|
static void scard_send_EstablishContext(IRP *irp);
|
||||||
|
static void scard_send_ListReaders(IRP *irp, int wide);
|
||||||
|
static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl);
|
||||||
|
static int scard_add_new_device(tui32 device_id);
|
||||||
|
static int scard_get_free_slot();
|
||||||
|
static void scard_release_resources();
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
** non static functions **
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void scard_device_announce(tui32 device_id)
|
||||||
|
{
|
||||||
|
IRP *irp;
|
||||||
|
|
||||||
|
log_debug("entered: device_id=%d", device_id);
|
||||||
|
|
||||||
|
if (!g_smartcards_inited)
|
||||||
|
{
|
||||||
|
g_memset(&smartcards, 0, sizeof(smartcards));
|
||||||
|
g_smartcards_inited = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((irp = devredir_irp_new()) == NULL)
|
||||||
|
{
|
||||||
|
log_error("system out of memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
irp->scard_index = scard_add_new_device(device_id);
|
||||||
|
if (irp->scard_index < 0)
|
||||||
|
{
|
||||||
|
log_debug("NOT adding smartcard with DeviceId=%d to list", device_id);
|
||||||
|
devredir_irp_delete(irp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("added smartcard with DeviceId=%d to list", device_id);
|
||||||
|
|
||||||
|
irp->CompletionId = g_completion_id++;
|
||||||
|
irp->DeviceId = device_id;
|
||||||
|
irp->callback = scard_handle_EstablishContext_Return;
|
||||||
|
|
||||||
|
scard_send_EstablishContext(irp);
|
||||||
|
log_debug("leaving");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
** callbacks into this module **
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp,
|
||||||
|
tui32 DeviceId, tui32 CompletionId,
|
||||||
|
tui32 IoStatus)
|
||||||
|
{
|
||||||
|
tui32 len;
|
||||||
|
int tmp;
|
||||||
|
SMARTCARD *sc;
|
||||||
|
|
||||||
|
log_debug("entered");
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
||||||
|
{
|
||||||
|
log_error("DeviceId/CompletionId do not match those in IRP");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IoStatus != 0)
|
||||||
|
{
|
||||||
|
log_error("failed to establish context - device not usable");
|
||||||
|
/* LK_TODO delete irp and smartcard entry */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc = smartcards[irp->scard_index];
|
||||||
|
|
||||||
|
/* get OutputBufferLen */
|
||||||
|
xstream_rd_u32_le(s, len);
|
||||||
|
|
||||||
|
/* LK_TODO */
|
||||||
|
g_hexdump(s->p, len);
|
||||||
|
|
||||||
|
xstream_rd_u32_le(s, tmp); /* should be len 8, LE, V1 */
|
||||||
|
xstream_rd_u32_le(s, tmp); /* marshalling flag */
|
||||||
|
xstream_rd_u32_le(s, tmp); /* ?? */
|
||||||
|
xstream_rd_u32_le(s, tmp); /* ?? */
|
||||||
|
xstream_rd_u32_le(s, tmp); /* ?? */
|
||||||
|
xstream_rd_u32_le(s, tmp); /* ?? */
|
||||||
|
xstream_rd_u32_le(s, tmp); /* ?? */
|
||||||
|
xstream_rd_u32_le(s, len); /* len of context in bytes */
|
||||||
|
sc->Context_len = len;
|
||||||
|
xstream_copyout(sc->Context, s, len);
|
||||||
|
|
||||||
|
if (LOG_LEVEL == LOG_DEBUG)
|
||||||
|
{
|
||||||
|
log_debug("dumping context (%d bytes)", sc->Context_len);
|
||||||
|
g_hexdump(sc->Context, sc->Context_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
irp->callback = scard_handle_ListReaders_Return;
|
||||||
|
scard_send_ListReaders(irp, 1);
|
||||||
|
|
||||||
|
/* LK_TODO need to delete IRP */
|
||||||
|
log_debug("leaving");
|
||||||
|
}
|
||||||
|
|
||||||
|
void scard_handle_ListReaders_Return(struct stream *s, IRP *irp,
|
||||||
|
tui32 DeviceId, tui32 CompletionId,
|
||||||
|
tui32 IoStatus)
|
||||||
|
{
|
||||||
|
tui32 len;
|
||||||
|
|
||||||
|
log_debug("entered");
|
||||||
|
|
||||||
|
/* sanity check */
|
||||||
|
if ((DeviceId != irp->DeviceId) || (CompletionId != irp->CompletionId))
|
||||||
|
{
|
||||||
|
log_error("DeviceId/CompletionId do not match those in IRP");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IoStatus != 0)
|
||||||
|
{
|
||||||
|
log_error("failed to list readers - device not usable");
|
||||||
|
/* LK_TODO delete irp and smartcard entry */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get OutputBufferLen */
|
||||||
|
xstream_rd_u32_le(s, len);
|
||||||
|
|
||||||
|
/* LK_TODO */
|
||||||
|
log_debug("dumping %d bytes", len);
|
||||||
|
g_hexdump(s->p, len);
|
||||||
|
|
||||||
|
log_debug("leaving");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
** static functions local to this file **
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static void scard_send_EstablishContext(IRP *irp)
|
||||||
|
{
|
||||||
|
struct stream *s;
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
if ((s = scard_make_new_ioctl(irp, SCARD_IOCTL_ESTABLISH_CONTEXT)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
xstream_wr_u32_le(s, 0x08); /* len */
|
||||||
|
xstream_wr_u32_le(s, 0); /* unused */
|
||||||
|
xstream_wr_u32_le(s, SCARD_SCOPE_SYSTEM); /* Ioctl specific data */
|
||||||
|
xstream_wr_u32_le(s, 0); /* don't know what this is, */
|
||||||
|
/* but Win7 is sending it */
|
||||||
|
/* get stream len */
|
||||||
|
bytes = xstream_len(s);
|
||||||
|
|
||||||
|
/* InputBufferLength is number of bytes AFTER 20 byte padding */
|
||||||
|
*(s->data + 28) = bytes - 56;
|
||||||
|
|
||||||
|
/* send to client */
|
||||||
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
||||||
|
xstream_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static void scard_send_ListReaders(IRP *irp, int wide)
|
||||||
|
{
|
||||||
|
/* see [MS-RDPESC] 2.2.2.4 */
|
||||||
|
|
||||||
|
SMARTCARD *sc;
|
||||||
|
struct stream *s;
|
||||||
|
int bytes;
|
||||||
|
tui32 ioctl;
|
||||||
|
|
||||||
|
if ((sc = smartcards[irp->scard_index]) == NULL)
|
||||||
|
{
|
||||||
|
log_error("smartcards[%d] is NULL", irp->scard_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl = (wide > 0) ? SCARD_IOCTL_LIST_READERS_W :
|
||||||
|
SCARD_IOCTL_LIST_READERS_A;
|
||||||
|
|
||||||
|
if ((s = scard_make_new_ioctl(irp, ioctl)) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
xstream_wr_u32_le(s, 72); /* number of bytes to follow */
|
||||||
|
xstream_seek(s, 0x1c); /* freerdp does not use this */
|
||||||
|
|
||||||
|
/* insert context */
|
||||||
|
xstream_wr_u32_le(s, sc->Context_len);
|
||||||
|
xstream_copyin(s, sc->Context, sc->Context_len);
|
||||||
|
|
||||||
|
xstream_wr_u32_le(s, 36); /* length of mszGroups */
|
||||||
|
xstream_wr_u16_le(s, 0x0053);
|
||||||
|
xstream_wr_u16_le(s, 0x0043);
|
||||||
|
xstream_wr_u16_le(s, 0x0061);
|
||||||
|
xstream_wr_u16_le(s, 0x0072);
|
||||||
|
xstream_wr_u16_le(s, 0x0064);
|
||||||
|
xstream_wr_u16_le(s, 0x0024);
|
||||||
|
xstream_wr_u16_le(s, 0x0041);
|
||||||
|
xstream_wr_u16_le(s, 0x006c);
|
||||||
|
xstream_wr_u16_le(s, 0x006c);
|
||||||
|
xstream_wr_u16_le(s, 0x0052);
|
||||||
|
xstream_wr_u16_le(s, 0x0065);
|
||||||
|
xstream_wr_u16_le(s, 0x0061);
|
||||||
|
xstream_wr_u16_le(s, 0x0064);
|
||||||
|
xstream_wr_u16_le(s, 0x0065);
|
||||||
|
xstream_wr_u16_le(s, 0x0072);
|
||||||
|
xstream_wr_u16_le(s, 0x0073);
|
||||||
|
|
||||||
|
xstream_wr_u32_le(s, 0x00);
|
||||||
|
|
||||||
|
/* get stream len */
|
||||||
|
bytes = xstream_len(s);
|
||||||
|
|
||||||
|
/* InputBufferLength is number of bytes AFTER 20 byte padding */
|
||||||
|
*(s->data + 28) = bytes - 56;
|
||||||
|
|
||||||
|
/* send to client */
|
||||||
|
send_channel_data(g_rdpdr_chan_id, s->data, bytes);
|
||||||
|
xstream_free(s);
|
||||||
|
|
||||||
|
/*
|
||||||
|
scard_device_control: dumping 120 bytes of data
|
||||||
|
0000 00 08 00 00 58 00 00 00 2c 00 09 00 00 00 00 00 ....X...,.......
|
||||||
|
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||||
|
0020 01 10 08 00 cc cc cc cc 48 00 00 00 00 00 00 00 ........H.......
|
||||||
|
0030 04 00 00 00 00 00 02 00 24 00 00 00 04 00 02 00 ........$.......
|
||||||
|
0040 00 00 00 00 ff ff ff ff 04 00 00 00 84 db 03 01 ................
|
||||||
|
0050 24 00 00 00 53 00 43 00 61 00 72 00 64 00 24 00 $...S.C.a.r.d.$.
|
||||||
|
0060 41 00 6c 00 6c 00 52 00 65 00 61 00 64 00 65 00 A.l.l.R.e.a.d.e.
|
||||||
|
0070 72 00 73 00 00 00 00 00 r.s.....
|
||||||
|
scard_device_control: output_len=2048 input_len=88 ioctl_code=0x9002c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
scard_device_control: dumping 120 bytes of data
|
||||||
|
0000 00 08 00 00 80 00 00 00 14 00 09 00 00 00 00 00 ................
|
||||||
|
0010 2e 2e 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................
|
||||||
|
0020 01 10 08 00 cc cc cc cc 48 00 00 00 00 00 00 00 ........H.......
|
||||||
|
0030 02 00 00 00 00 00 00 00 72 64 00 00 00 00 00 00 ........rd......
|
||||||
|
0040 81 27 00 00 00 00 00 00 04 00 00 00 84 b3 03 01 .'..............
|
||||||
|
0050 24 00 00 00 53 00 43 00 61 00 72 00 64 00 24 00 $...S.C.a.r.d.$.
|
||||||
|
0060 41 00 6c 00 6c 00 52 00 65 00 61 00 64 00 65 00 A.l.l.R.e.a.d.e.
|
||||||
|
0070 72 00 73 00 00 00 00 00 r.s.....
|
||||||
|
scard_device_control: output_len=2048 input_len=128 ioctl_code=0x90014
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crate a new stream and insert specified IOCTL
|
||||||
|
*
|
||||||
|
* @param irp information about the I/O
|
||||||
|
* @param ioctl the IOCTL code
|
||||||
|
*
|
||||||
|
* @return stream with IOCTL inserted in it, NULL on error
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static struct stream *scard_make_new_ioctl(IRP *irp, tui32 ioctl)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* format of device control request
|
||||||
|
*
|
||||||
|
* DeviceIoRequest
|
||||||
|
* u16 RDPDR_CTYP_CORE
|
||||||
|
* u16 PAKID_CORE_DEVICE_IOREQUEST
|
||||||
|
* u32 DeviceId
|
||||||
|
* u32 FileId
|
||||||
|
* u32 CompletionId
|
||||||
|
* u32 MajorFunction
|
||||||
|
* u32 MinorFunction
|
||||||
|
*
|
||||||
|
* u32 OutputBufferLength SHOULD be 2048
|
||||||
|
* u32 InputBufferLength
|
||||||
|
* u32 IoControlCode
|
||||||
|
* 20 bytes padding
|
||||||
|
* xx bytes InputBuffer (variable)
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct stream *s;
|
||||||
|
|
||||||
|
xstream_new(s, 1024 * 3);
|
||||||
|
if (s == NULL)
|
||||||
|
{
|
||||||
|
log_error("system out of memory");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
devredir_insert_DeviceIoRequest(s,
|
||||||
|
irp->DeviceId,
|
||||||
|
irp->FileId,
|
||||||
|
irp->CompletionId,
|
||||||
|
IRP_MJ_DEVICE_CONTROL,
|
||||||
|
0);
|
||||||
|
|
||||||
|
xstream_wr_u32_le(s, 2048); /* OutputBufferLength */
|
||||||
|
xstream_wr_u32_le(s, 0); /* InputBufferLength - insert later */
|
||||||
|
xstream_wr_u32_le(s, ioctl); /* Ioctl Code */
|
||||||
|
xstream_seek(s, 20); /* padding */
|
||||||
|
|
||||||
|
/* [MS-RPCE] 2.2.6.1 */
|
||||||
|
xstream_wr_u32_le(s, 0x00081001); /* len 8, LE, v1 */
|
||||||
|
xstream_wr_u32_le(s, 0xcccccccc); /* filler */
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new smart card device entry and insert it into smartcards[]
|
||||||
|
*
|
||||||
|
* @param device_id DeviceId of new card
|
||||||
|
*
|
||||||
|
* @return index into smartcards[] on success, -1 on failure
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static int scard_add_new_device(tui32 device_id)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
SMARTCARD *sc;
|
||||||
|
|
||||||
|
if ((index = scard_get_free_slot()) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((sc = g_malloc(sizeof(SMARTCARD), 1)) == NULL)
|
||||||
|
{
|
||||||
|
log_error("system out of memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc->DeviceId = device_id;
|
||||||
|
smartcards[index] = sc;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find first unused entry in smartcards
|
||||||
|
*
|
||||||
|
* @return index of first unused entry in smartcards or -1 if smartcards is full
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static int scard_get_free_slot()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_SMARTCARDS; i++)
|
||||||
|
{
|
||||||
|
if (smartcards[i] == NULL)
|
||||||
|
{
|
||||||
|
log_debug("found free slot at index %d", i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error("too many smart card devices; rejecting this one");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release resources prior to shutting down
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
static void scard_release_resources()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_SMARTCARDS; i++)
|
||||||
|
{
|
||||||
|
if (smartcards[i] != NULL)
|
||||||
|
{
|
||||||
|
g_free(smartcards[i]);
|
||||||
|
smartcards[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
42
sesman/chansrv/smartcard.h
Normal file
42
sesman/chansrv/smartcard.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* xrdp: A Remote Desktop Protocol server.
|
||||||
|
*
|
||||||
|
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* smartcard redirection support
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SMARTCARD_C
|
||||||
|
#define _SMARTCARD_C
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
#include "irp.h"
|
||||||
|
|
||||||
|
/* forward declarations */
|
||||||
|
void scard_device_announce(tui32 device_id);
|
||||||
|
|
||||||
|
/* callbacks into this module */
|
||||||
|
void scard_handle_EstablishContext_Return(struct stream *s, IRP *irp,
|
||||||
|
tui32 DeviceId, tui32 CompletionId,
|
||||||
|
tui32 IoStatus);
|
||||||
|
|
||||||
|
void scard_handle_ListReaders_Return(struct stream *s, IRP *irp,
|
||||||
|
tui32 DeviceId, tui32 CompletionId,
|
||||||
|
tui32 IoStatus);
|
||||||
|
|
||||||
|
#endif /* end #ifndef _SMARTCARD_C */
|
Loading…
Reference in New Issue
Block a user