1a61ed2ceSHans Rosenfeld /*
2a61ed2ceSHans Rosenfeld * This file and its contents are supplied under the terms of the
3a61ed2ceSHans Rosenfeld * Common Development and Distribution License ("CDDL"), version 1.0.
4a61ed2ceSHans Rosenfeld * You may only use this file in accordance with the terms of version
5a61ed2ceSHans Rosenfeld * 1.0 of the CDDL.
6a61ed2ceSHans Rosenfeld *
7a61ed2ceSHans Rosenfeld * A full copy of the text of the CDDL should have accompanied this
8a61ed2ceSHans Rosenfeld * source. A copy of the CDDL is also available via the Internet at
9a61ed2ceSHans Rosenfeld * http://www.illumos.org/license/CDDL.
10a61ed2ceSHans Rosenfeld */
11a61ed2ceSHans Rosenfeld
12a61ed2ceSHans Rosenfeld /*
13a61ed2ceSHans Rosenfeld * Copyright 2019, Joyent, Inc.
14118b2dbfSJoshua M. Clulow * Copyright 2022 Oxide Computer Company
15a61ed2ceSHans Rosenfeld */
16a61ed2ceSHans Rosenfeld
17a61ed2ceSHans Rosenfeld #include <stdlib.h>
18118b2dbfSJoshua M. Clulow #include <stdio.h>
19118b2dbfSJoshua M. Clulow #include <stdbool.h>
20a61ed2ceSHans Rosenfeld #include <sys/types.h>
21a61ed2ceSHans Rosenfeld #include <sys/stat.h>
22118b2dbfSJoshua M. Clulow #include <sys/list.h>
23a61ed2ceSHans Rosenfeld #include <fcntl.h>
24a61ed2ceSHans Rosenfeld #include <fts.h>
25a61ed2ceSHans Rosenfeld #include <errno.h>
26a61ed2ceSHans Rosenfeld #include <strings.h>
27a61ed2ceSHans Rosenfeld #include <unistd.h>
28118b2dbfSJoshua M. Clulow #include <upanic.h>
29a61ed2ceSHans Rosenfeld #include <sys/debug.h>
30a61ed2ceSHans Rosenfeld #include <sys/filio.h>
31a61ed2ceSHans Rosenfeld #include <sys/usb/clients/ccid/uccid.h>
32a61ed2ceSHans Rosenfeld
33a61ed2ceSHans Rosenfeld #include <winscard.h>
34a61ed2ceSHans Rosenfeld
35a61ed2ceSHans Rosenfeld /*
36a61ed2ceSHans Rosenfeld * Implementation of the PCSC library leveraging the uccid framework.
37a61ed2ceSHans Rosenfeld */
38a61ed2ceSHans Rosenfeld
39a61ed2ceSHans Rosenfeld typedef struct pcsc_hdl {
40a61ed2ceSHans Rosenfeld hrtime_t pcsc_create_time;
41118b2dbfSJoshua M. Clulow list_t pcsc_autoalloc;
42118b2dbfSJoshua M. Clulow list_t pcsc_cards;
43a61ed2ceSHans Rosenfeld } pcsc_hdl_t;
44a61ed2ceSHans Rosenfeld
45a61ed2ceSHans Rosenfeld typedef struct pcsc_card {
46118b2dbfSJoshua M. Clulow list_node_t pcc_link;
47118b2dbfSJoshua M. Clulow pcsc_hdl_t *pcc_hdl;
48a61ed2ceSHans Rosenfeld int pcc_fd;
49118b2dbfSJoshua M. Clulow char *pcc_name;
50118b2dbfSJoshua M. Clulow size_t pcc_namelen;
51a61ed2ceSHans Rosenfeld } pcsc_card_t;
52a61ed2ceSHans Rosenfeld
53118b2dbfSJoshua M. Clulow typedef struct pcsc_mem {
54118b2dbfSJoshua M. Clulow list_node_t pcm_link;
55118b2dbfSJoshua M. Clulow void *pcm_buf;
56118b2dbfSJoshua M. Clulow } pcsc_mem_t;
57118b2dbfSJoshua M. Clulow
58a61ed2ceSHans Rosenfeld /*
59a61ed2ceSHans Rosenfeld * Required globals
60a61ed2ceSHans Rosenfeld */
61a61ed2ceSHans Rosenfeld SCARD_IO_REQUEST g_rgSCardT0Pci = {
62a61ed2ceSHans Rosenfeld SCARD_PROTOCOL_T0,
63a61ed2ceSHans Rosenfeld 0
64a61ed2ceSHans Rosenfeld };
65a61ed2ceSHans Rosenfeld
66a61ed2ceSHans Rosenfeld SCARD_IO_REQUEST g_rgSCardT1Pci = {
67a61ed2ceSHans Rosenfeld SCARD_PROTOCOL_T1,
68a61ed2ceSHans Rosenfeld 0
69a61ed2ceSHans Rosenfeld };
70a61ed2ceSHans Rosenfeld
71a61ed2ceSHans Rosenfeld SCARD_IO_REQUEST g_rgSCardRawPci = {
72a61ed2ceSHans Rosenfeld SCARD_PROTOCOL_RAW,
73a61ed2ceSHans Rosenfeld 0
74a61ed2ceSHans Rosenfeld };
75a61ed2ceSHans Rosenfeld
76a61ed2ceSHans Rosenfeld const char *
pcsc_stringify_error(const LONG err)77a61ed2ceSHans Rosenfeld pcsc_stringify_error(const LONG err)
78a61ed2ceSHans Rosenfeld {
79a61ed2ceSHans Rosenfeld switch (err) {
80a61ed2ceSHans Rosenfeld case SCARD_S_SUCCESS:
81a61ed2ceSHans Rosenfeld return ("no error");
82a61ed2ceSHans Rosenfeld case SCARD_F_INTERNAL_ERROR:
83a61ed2ceSHans Rosenfeld return ("internal error");
84a61ed2ceSHans Rosenfeld case SCARD_E_CANCELLED:
85a61ed2ceSHans Rosenfeld return ("request cancelled");
86a61ed2ceSHans Rosenfeld case SCARD_E_INVALID_HANDLE:
87a61ed2ceSHans Rosenfeld return ("invalid handle");
88a61ed2ceSHans Rosenfeld case SCARD_E_INVALID_PARAMETER:
89a61ed2ceSHans Rosenfeld return ("invalid parameter");
90a61ed2ceSHans Rosenfeld case SCARD_E_NO_MEMORY:
91a61ed2ceSHans Rosenfeld return ("no memory");
92a61ed2ceSHans Rosenfeld case SCARD_E_INSUFFICIENT_BUFFER:
93a61ed2ceSHans Rosenfeld return ("buffer was insufficiently sized");
94a61ed2ceSHans Rosenfeld case SCARD_E_INVALID_VALUE:
95a61ed2ceSHans Rosenfeld return ("invalid value passed");
96a61ed2ceSHans Rosenfeld case SCARD_E_UNKNOWN_READER:
97a61ed2ceSHans Rosenfeld return ("unknown reader");
98a61ed2ceSHans Rosenfeld case SCARD_E_TIMEOUT:
99a61ed2ceSHans Rosenfeld return ("timeout occurred");
100a61ed2ceSHans Rosenfeld case SCARD_E_SHARING_VIOLATION:
101a61ed2ceSHans Rosenfeld return ("sharing violation");
102a61ed2ceSHans Rosenfeld case SCARD_E_NO_SMARTCARD:
103a61ed2ceSHans Rosenfeld return ("no smartcard present");
104a61ed2ceSHans Rosenfeld case SCARD_E_UNKNOWN_CARD:
105a61ed2ceSHans Rosenfeld return ("unknown ICC");
106a61ed2ceSHans Rosenfeld case SCARD_E_PROTO_MISMATCH:
107a61ed2ceSHans Rosenfeld return ("protocol mismatch");
108a61ed2ceSHans Rosenfeld case SCARD_F_COMM_ERROR:
109a61ed2ceSHans Rosenfeld return ("communication error");
110a61ed2ceSHans Rosenfeld case SCARD_F_UNKNOWN_ERROR:
111a61ed2ceSHans Rosenfeld return ("unknown error");
112a61ed2ceSHans Rosenfeld case SCARD_E_READER_UNAVAILABLE:
113a61ed2ceSHans Rosenfeld return ("reader unavailable");
114a61ed2ceSHans Rosenfeld case SCARD_E_NO_SERVICE:
115a61ed2ceSHans Rosenfeld return ("service error");
116a61ed2ceSHans Rosenfeld case SCARD_E_UNSUPPORTED_FEATURE:
117a61ed2ceSHans Rosenfeld return ("ICC requires unsupported feature");
118a61ed2ceSHans Rosenfeld case SCARD_E_NO_READERS_AVAILABLE:
119a61ed2ceSHans Rosenfeld return ("no readers avaiable");
120a61ed2ceSHans Rosenfeld case SCARD_W_UNSUPPORTED_CARD:
121a61ed2ceSHans Rosenfeld return ("ICC unsupported");
122a61ed2ceSHans Rosenfeld case SCARD_W_UNPOWERED_CARD:
123a61ed2ceSHans Rosenfeld return ("ICC is not powered");
124a61ed2ceSHans Rosenfeld case SCARD_W_RESET_CARD:
125a61ed2ceSHans Rosenfeld return ("ICC was reset");
126a61ed2ceSHans Rosenfeld case SCARD_W_REMOVED_CARD:
127a61ed2ceSHans Rosenfeld return ("ICC has been removed");
128a61ed2ceSHans Rosenfeld default:
129a61ed2ceSHans Rosenfeld return ("unknown error");
130a61ed2ceSHans Rosenfeld }
131a61ed2ceSHans Rosenfeld }
132a61ed2ceSHans Rosenfeld
133118b2dbfSJoshua M. Clulow /*
134118b2dbfSJoshua M. Clulow * Allocate a buffer of size "len" for use with an SCARD_AUTOALLOCATE
135118b2dbfSJoshua M. Clulow * parameter. Each automatically allocated buffer must be appended to the
136118b2dbfSJoshua M. Clulow * context buffer list so that it can be freed during the call to
137118b2dbfSJoshua M. Clulow * SCardReleaseContext().
138118b2dbfSJoshua M. Clulow */
139118b2dbfSJoshua M. Clulow static void *
pcsc_mem_alloc(pcsc_hdl_t * hdl,size_t len)140118b2dbfSJoshua M. Clulow pcsc_mem_alloc(pcsc_hdl_t *hdl, size_t len)
141118b2dbfSJoshua M. Clulow {
142118b2dbfSJoshua M. Clulow pcsc_mem_t *mem;
143118b2dbfSJoshua M. Clulow
144118b2dbfSJoshua M. Clulow if ((mem = malloc(sizeof (*mem))) == NULL) {
145118b2dbfSJoshua M. Clulow return (NULL);
146118b2dbfSJoshua M. Clulow }
147118b2dbfSJoshua M. Clulow
148118b2dbfSJoshua M. Clulow if ((mem->pcm_buf = malloc(len)) == NULL) {
149118b2dbfSJoshua M. Clulow free(mem);
150118b2dbfSJoshua M. Clulow return (NULL);
151118b2dbfSJoshua M. Clulow }
152118b2dbfSJoshua M. Clulow list_link_init(&mem->pcm_link);
153118b2dbfSJoshua M. Clulow
154118b2dbfSJoshua M. Clulow /*
155118b2dbfSJoshua M. Clulow * Put the buffer on the per-context list:
156118b2dbfSJoshua M. Clulow */
157118b2dbfSJoshua M. Clulow list_insert_tail(&hdl->pcsc_autoalloc, mem);
158118b2dbfSJoshua M. Clulow
159118b2dbfSJoshua M. Clulow return (mem->pcm_buf);
160118b2dbfSJoshua M. Clulow }
161118b2dbfSJoshua M. Clulow
162118b2dbfSJoshua M. Clulow static void
pcsc_mem_free(pcsc_hdl_t * hdl,void * buf)163118b2dbfSJoshua M. Clulow pcsc_mem_free(pcsc_hdl_t *hdl, void *buf)
164118b2dbfSJoshua M. Clulow {
165118b2dbfSJoshua M. Clulow for (pcsc_mem_t *mem = list_head(&hdl->pcsc_autoalloc); mem != NULL;
166118b2dbfSJoshua M. Clulow mem = list_next(&hdl->pcsc_autoalloc, mem)) {
167118b2dbfSJoshua M. Clulow if (mem->pcm_buf == buf) {
168118b2dbfSJoshua M. Clulow list_remove(&hdl->pcsc_autoalloc, mem);
169118b2dbfSJoshua M. Clulow free(mem->pcm_buf);
170118b2dbfSJoshua M. Clulow free(mem);
171118b2dbfSJoshua M. Clulow return;
172118b2dbfSJoshua M. Clulow }
173118b2dbfSJoshua M. Clulow }
174118b2dbfSJoshua M. Clulow
175118b2dbfSJoshua M. Clulow char msg[512];
176118b2dbfSJoshua M. Clulow (void) snprintf(msg, sizeof (msg), "freed buffer %p not in context %p",
177118b2dbfSJoshua M. Clulow buf, hdl);
178118b2dbfSJoshua M. Clulow upanic(msg, strlen(msg));
179118b2dbfSJoshua M. Clulow }
180118b2dbfSJoshua M. Clulow
181118b2dbfSJoshua M. Clulow static pcsc_card_t *
pcsc_card_alloc(pcsc_hdl_t * hdl,const char * reader)182118b2dbfSJoshua M. Clulow pcsc_card_alloc(pcsc_hdl_t *hdl, const char *reader)
183118b2dbfSJoshua M. Clulow {
184118b2dbfSJoshua M. Clulow pcsc_card_t *card;
185118b2dbfSJoshua M. Clulow
186118b2dbfSJoshua M. Clulow if ((card = malloc(sizeof (*card))) == NULL) {
187118b2dbfSJoshua M. Clulow return (NULL);
188118b2dbfSJoshua M. Clulow }
189118b2dbfSJoshua M. Clulow card->pcc_hdl = hdl;
190118b2dbfSJoshua M. Clulow card->pcc_fd = -1;
191118b2dbfSJoshua M. Clulow list_link_init(&card->pcc_link);
192118b2dbfSJoshua M. Clulow
193118b2dbfSJoshua M. Clulow /*
194118b2dbfSJoshua M. Clulow * The reader name is returned as a multi-string, which means we need
195118b2dbfSJoshua M. Clulow * the regular C string and then an additional null termination byte to
196118b2dbfSJoshua M. Clulow * end the list of strings:
197118b2dbfSJoshua M. Clulow */
198118b2dbfSJoshua M. Clulow card->pcc_namelen = strlen(reader) + 2;
199118b2dbfSJoshua M. Clulow if ((card->pcc_name = malloc(card->pcc_namelen)) == NULL) {
200118b2dbfSJoshua M. Clulow free(card);
201118b2dbfSJoshua M. Clulow return (NULL);
202118b2dbfSJoshua M. Clulow }
203118b2dbfSJoshua M. Clulow bcopy(reader, card->pcc_name, card->pcc_namelen - 1);
204118b2dbfSJoshua M. Clulow card->pcc_name[card->pcc_namelen - 1] = '\0';
205118b2dbfSJoshua M. Clulow
206118b2dbfSJoshua M. Clulow /*
207118b2dbfSJoshua M. Clulow * Insert the card handle into the per-context list so that we can free
208118b2dbfSJoshua M. Clulow * them later during SCardReleaseContext().
209118b2dbfSJoshua M. Clulow */
210118b2dbfSJoshua M. Clulow list_insert_tail(&hdl->pcsc_cards, card);
211118b2dbfSJoshua M. Clulow
212118b2dbfSJoshua M. Clulow return (card);
213118b2dbfSJoshua M. Clulow }
214118b2dbfSJoshua M. Clulow
215118b2dbfSJoshua M. Clulow static void
pcsc_card_free(pcsc_card_t * card)216118b2dbfSJoshua M. Clulow pcsc_card_free(pcsc_card_t *card)
217118b2dbfSJoshua M. Clulow {
218118b2dbfSJoshua M. Clulow if (card == NULL) {
219118b2dbfSJoshua M. Clulow return;
220118b2dbfSJoshua M. Clulow }
221118b2dbfSJoshua M. Clulow
222118b2dbfSJoshua M. Clulow if (card->pcc_fd >= 0) {
223118b2dbfSJoshua M. Clulow (void) close(card->pcc_fd);
224118b2dbfSJoshua M. Clulow }
225118b2dbfSJoshua M. Clulow
226118b2dbfSJoshua M. Clulow /*
227118b2dbfSJoshua M. Clulow * Remove the card handle from the per-context list:
228118b2dbfSJoshua M. Clulow */
229118b2dbfSJoshua M. Clulow pcsc_hdl_t *hdl = card->pcc_hdl;
230118b2dbfSJoshua M. Clulow list_remove(&hdl->pcsc_cards, card);
231118b2dbfSJoshua M. Clulow
232118b2dbfSJoshua M. Clulow free(card->pcc_name);
233118b2dbfSJoshua M. Clulow free(card);
234118b2dbfSJoshua M. Clulow }
235a61ed2ceSHans Rosenfeld
236a61ed2ceSHans Rosenfeld /*
237a61ed2ceSHans Rosenfeld * This is called when a caller wishes to open a new Library context.
238a61ed2ceSHans Rosenfeld */
239a61ed2ceSHans Rosenfeld LONG
SCardEstablishContext(DWORD scope,LPCVOID unused0,LPCVOID unused1,LPSCARDCONTEXT outp)240a61ed2ceSHans Rosenfeld SCardEstablishContext(DWORD scope, LPCVOID unused0, LPCVOID unused1,
241a61ed2ceSHans Rosenfeld LPSCARDCONTEXT outp)
242a61ed2ceSHans Rosenfeld {
243a61ed2ceSHans Rosenfeld pcsc_hdl_t *hdl;
244a61ed2ceSHans Rosenfeld
245a61ed2ceSHans Rosenfeld if (outp == NULL) {
246a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
247a61ed2ceSHans Rosenfeld }
248a61ed2ceSHans Rosenfeld
249a61ed2ceSHans Rosenfeld if (scope != SCARD_SCOPE_SYSTEM) {
250a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
251a61ed2ceSHans Rosenfeld }
252a61ed2ceSHans Rosenfeld
253a61ed2ceSHans Rosenfeld hdl = calloc(1, sizeof (pcsc_hdl_t));
254a61ed2ceSHans Rosenfeld if (hdl == NULL) {
255a61ed2ceSHans Rosenfeld return (SCARD_E_NO_MEMORY);
256a61ed2ceSHans Rosenfeld }
257118b2dbfSJoshua M. Clulow list_create(&hdl->pcsc_autoalloc, sizeof (pcsc_mem_t),
258118b2dbfSJoshua M. Clulow offsetof(pcsc_mem_t, pcm_link));
259118b2dbfSJoshua M. Clulow list_create(&hdl->pcsc_cards, sizeof (pcsc_card_t),
260118b2dbfSJoshua M. Clulow offsetof(pcsc_card_t, pcc_link));
261a61ed2ceSHans Rosenfeld
262a61ed2ceSHans Rosenfeld hdl->pcsc_create_time = gethrtime();
263a61ed2ceSHans Rosenfeld *outp = hdl;
264a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
265a61ed2ceSHans Rosenfeld }
266a61ed2ceSHans Rosenfeld
267118b2dbfSJoshua M. Clulow bool
pcsc_valid_context(SCARDCONTEXT hdl)268118b2dbfSJoshua M. Clulow pcsc_valid_context(SCARDCONTEXT hdl)
269118b2dbfSJoshua M. Clulow {
270118b2dbfSJoshua M. Clulow /*
271118b2dbfSJoshua M. Clulow * On some other platforms, the context handle is a signed integer.
272118b2dbfSJoshua M. Clulow * Some software has been observed to use -1 as an invalid handle
273118b2dbfSJoshua M. Clulow * sentinel value, so we need to explicitly handle that here.
274118b2dbfSJoshua M. Clulow */
275118b2dbfSJoshua M. Clulow return (hdl != NULL && (uintptr_t)hdl != UINTPTR_MAX);
276118b2dbfSJoshua M. Clulow }
277118b2dbfSJoshua M. Clulow
278118b2dbfSJoshua M. Clulow LONG
SCardIsValidContext(SCARDCONTEXT hdl)279118b2dbfSJoshua M. Clulow SCardIsValidContext(SCARDCONTEXT hdl)
280118b2dbfSJoshua M. Clulow {
281118b2dbfSJoshua M. Clulow if (!pcsc_valid_context(hdl)) {
282118b2dbfSJoshua M. Clulow return (SCARD_E_INVALID_HANDLE);
283118b2dbfSJoshua M. Clulow }
284118b2dbfSJoshua M. Clulow
285118b2dbfSJoshua M. Clulow return (SCARD_S_SUCCESS);
286118b2dbfSJoshua M. Clulow }
287118b2dbfSJoshua M. Clulow
288a61ed2ceSHans Rosenfeld /*
289a61ed2ceSHans Rosenfeld * This is called to free a library context from a client.
290a61ed2ceSHans Rosenfeld */
291a61ed2ceSHans Rosenfeld LONG
SCardReleaseContext(SCARDCONTEXT arg)292118b2dbfSJoshua M. Clulow SCardReleaseContext(SCARDCONTEXT arg)
293a61ed2ceSHans Rosenfeld {
294118b2dbfSJoshua M. Clulow if (!pcsc_valid_context(arg)) {
295118b2dbfSJoshua M. Clulow return (SCARD_E_INVALID_HANDLE);
296118b2dbfSJoshua M. Clulow }
297118b2dbfSJoshua M. Clulow
298118b2dbfSJoshua M. Clulow /*
299118b2dbfSJoshua M. Clulow * Free any SCARD_AUTOALLOCATE memory now.
300118b2dbfSJoshua M. Clulow */
301118b2dbfSJoshua M. Clulow pcsc_hdl_t *hdl = arg;
302118b2dbfSJoshua M. Clulow pcsc_mem_t *mem;
303118b2dbfSJoshua M. Clulow while ((mem = list_head(&hdl->pcsc_autoalloc)) != NULL) {
304118b2dbfSJoshua M. Clulow pcsc_mem_free(hdl, mem->pcm_buf);
305118b2dbfSJoshua M. Clulow }
306118b2dbfSJoshua M. Clulow list_destroy(&hdl->pcsc_autoalloc);
307118b2dbfSJoshua M. Clulow
308118b2dbfSJoshua M. Clulow /*
309118b2dbfSJoshua M. Clulow * Free any card handles that were not explicitly freed:
310118b2dbfSJoshua M. Clulow */
311118b2dbfSJoshua M. Clulow pcsc_card_t *card;
312118b2dbfSJoshua M. Clulow while ((card = list_head(&hdl->pcsc_cards)) != NULL) {
313118b2dbfSJoshua M. Clulow pcsc_card_free(card);
314118b2dbfSJoshua M. Clulow }
315118b2dbfSJoshua M. Clulow list_destroy(&hdl->pcsc_cards);
316118b2dbfSJoshua M. Clulow
317a61ed2ceSHans Rosenfeld free(hdl);
318a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
319a61ed2ceSHans Rosenfeld }
320a61ed2ceSHans Rosenfeld
321a61ed2ceSHans Rosenfeld /*
322a61ed2ceSHans Rosenfeld * This is called to release memory allocated by the library. No, it doesn't
323a61ed2ceSHans Rosenfeld * make sense to take a const pointer when being given memory to free. It just
324a61ed2ceSHans Rosenfeld * means we have to cast it, but remember: this isn't our API.
325a61ed2ceSHans Rosenfeld */
326a61ed2ceSHans Rosenfeld LONG
SCardFreeMemory(SCARDCONTEXT hdl,LPCVOID mem)327118b2dbfSJoshua M. Clulow SCardFreeMemory(SCARDCONTEXT hdl, LPCVOID mem)
328a61ed2ceSHans Rosenfeld {
329118b2dbfSJoshua M. Clulow if (!pcsc_valid_context(hdl)) {
330118b2dbfSJoshua M. Clulow return (SCARD_E_INVALID_HANDLE);
331118b2dbfSJoshua M. Clulow }
332118b2dbfSJoshua M. Clulow
333118b2dbfSJoshua M. Clulow pcsc_mem_free(hdl, (void *)mem);
334a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
335a61ed2ceSHans Rosenfeld }
336a61ed2ceSHans Rosenfeld
337a61ed2ceSHans Rosenfeld /*
338a61ed2ceSHans Rosenfeld * This is called by a caller to get a list of readers that exist in the system.
339a61ed2ceSHans Rosenfeld * If lenp is set to SCARD_AUTOALLOCATE, then we are responsible for dealing
340a61ed2ceSHans Rosenfeld * with this memory.
341a61ed2ceSHans Rosenfeld */
342a61ed2ceSHans Rosenfeld LONG
SCardListReaders(SCARDCONTEXT arg,LPCSTR groups,LPSTR bufp,LPDWORD lenp)343118b2dbfSJoshua M. Clulow SCardListReaders(SCARDCONTEXT arg, LPCSTR groups, LPSTR bufp, LPDWORD lenp)
344a61ed2ceSHans Rosenfeld {
345118b2dbfSJoshua M. Clulow pcsc_hdl_t *hdl = arg;
346a61ed2ceSHans Rosenfeld FTS *fts;
347a61ed2ceSHans Rosenfeld FTSENT *ent;
348a61ed2ceSHans Rosenfeld char *const root[] = { "/dev/ccid", NULL };
349a61ed2ceSHans Rosenfeld char *ubuf;
350a61ed2ceSHans Rosenfeld char **readers;
351a61ed2ceSHans Rosenfeld uint32_t len, ulen, npaths, nalloc, off, i;
352a61ed2ceSHans Rosenfeld int ret;
353a61ed2ceSHans Rosenfeld
354118b2dbfSJoshua M. Clulow if (!pcsc_valid_context(hdl)) {
355118b2dbfSJoshua M. Clulow return (SCARD_E_INVALID_HANDLE);
356118b2dbfSJoshua M. Clulow }
357118b2dbfSJoshua M. Clulow
358a61ed2ceSHans Rosenfeld if (groups != NULL || lenp == NULL) {
359a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
360a61ed2ceSHans Rosenfeld }
361a61ed2ceSHans Rosenfeld
362a61ed2ceSHans Rosenfeld fts = fts_open(root, FTS_LOGICAL | FTS_NOCHDIR, NULL);
363a61ed2ceSHans Rosenfeld if (fts == NULL) {
364a61ed2ceSHans Rosenfeld switch (errno) {
365a61ed2ceSHans Rosenfeld case ENOENT:
366a61ed2ceSHans Rosenfeld case ENOTDIR:
367a61ed2ceSHans Rosenfeld return (SCARD_E_NO_READERS_AVAILABLE);
368a61ed2ceSHans Rosenfeld case ENOMEM:
369a61ed2ceSHans Rosenfeld case EAGAIN:
370a61ed2ceSHans Rosenfeld return (SCARD_E_NO_MEMORY);
371a61ed2ceSHans Rosenfeld default:
372a61ed2ceSHans Rosenfeld return (SCARD_E_NO_SERVICE);
373a61ed2ceSHans Rosenfeld }
374a61ed2ceSHans Rosenfeld }
375a61ed2ceSHans Rosenfeld
376a61ed2ceSHans Rosenfeld npaths = nalloc = 0;
377a61ed2ceSHans Rosenfeld /*
378a61ed2ceSHans Rosenfeld * Account for the NUL we'll have to place at the end of this.
379a61ed2ceSHans Rosenfeld */
380a61ed2ceSHans Rosenfeld len = 1;
381a61ed2ceSHans Rosenfeld readers = NULL;
382a61ed2ceSHans Rosenfeld while ((ent = fts_read(fts)) != NULL) {
383a61ed2ceSHans Rosenfeld size_t plen;
384a61ed2ceSHans Rosenfeld
385a61ed2ceSHans Rosenfeld if (ent->fts_level != 2 || ent->fts_info == FTS_DP)
386a61ed2ceSHans Rosenfeld continue;
387a61ed2ceSHans Rosenfeld
388a61ed2ceSHans Rosenfeld if (ent->fts_info == FTS_ERR || ent->fts_info == FTS_NS)
389a61ed2ceSHans Rosenfeld continue;
390a61ed2ceSHans Rosenfeld
391a61ed2ceSHans Rosenfeld if (S_ISCHR(ent->fts_statp->st_mode) == 0)
392a61ed2ceSHans Rosenfeld continue;
393a61ed2ceSHans Rosenfeld
394a61ed2ceSHans Rosenfeld plen = strlen(ent->fts_path) + 1;
395a61ed2ceSHans Rosenfeld if (UINT32_MAX - len <= plen) {
396a61ed2ceSHans Rosenfeld /*
397a61ed2ceSHans Rosenfeld * I mean, it's true. But I wish I could just give you
398a61ed2ceSHans Rosenfeld * EOVERFLOW.
399a61ed2ceSHans Rosenfeld */
400a61ed2ceSHans Rosenfeld ret = SCARD_E_INSUFFICIENT_BUFFER;
401a61ed2ceSHans Rosenfeld goto out;
402a61ed2ceSHans Rosenfeld }
403a61ed2ceSHans Rosenfeld
404a61ed2ceSHans Rosenfeld if (npaths == nalloc) {
405a61ed2ceSHans Rosenfeld char **tmp;
406a61ed2ceSHans Rosenfeld
407a61ed2ceSHans Rosenfeld nalloc += 8;
408a61ed2ceSHans Rosenfeld tmp = reallocarray(readers, nalloc, sizeof (char *));
409a61ed2ceSHans Rosenfeld if (tmp == NULL) {
410a61ed2ceSHans Rosenfeld ret = SCARD_E_NO_MEMORY;
411a61ed2ceSHans Rosenfeld goto out;
412a61ed2ceSHans Rosenfeld }
413a61ed2ceSHans Rosenfeld readers = tmp;
414a61ed2ceSHans Rosenfeld }
415a61ed2ceSHans Rosenfeld readers[npaths] = strdup(ent->fts_path);
416a61ed2ceSHans Rosenfeld npaths++;
417a61ed2ceSHans Rosenfeld len += plen;
418a61ed2ceSHans Rosenfeld }
419a61ed2ceSHans Rosenfeld
420a61ed2ceSHans Rosenfeld if (npaths == 0) {
421a61ed2ceSHans Rosenfeld ret = SCARD_E_NO_READERS_AVAILABLE;
422a61ed2ceSHans Rosenfeld goto out;
423a61ed2ceSHans Rosenfeld }
424a61ed2ceSHans Rosenfeld
425a61ed2ceSHans Rosenfeld ulen = *lenp;
426a61ed2ceSHans Rosenfeld *lenp = len;
427a61ed2ceSHans Rosenfeld if (ulen != SCARD_AUTOALLOCATE) {
428a61ed2ceSHans Rosenfeld if (bufp == NULL) {
429a61ed2ceSHans Rosenfeld ret = SCARD_S_SUCCESS;
430a61ed2ceSHans Rosenfeld goto out;
431a61ed2ceSHans Rosenfeld }
432a61ed2ceSHans Rosenfeld
433a61ed2ceSHans Rosenfeld if (ulen < len) {
434a61ed2ceSHans Rosenfeld ret = SCARD_E_INSUFFICIENT_BUFFER;
435a61ed2ceSHans Rosenfeld goto out;
436a61ed2ceSHans Rosenfeld }
437a61ed2ceSHans Rosenfeld
438a61ed2ceSHans Rosenfeld ubuf = bufp;
439a61ed2ceSHans Rosenfeld } else {
440a61ed2ceSHans Rosenfeld char **bufpp;
441a61ed2ceSHans Rosenfeld if (bufp == NULL) {
442a61ed2ceSHans Rosenfeld ret = SCARD_E_INVALID_PARAMETER;
443a61ed2ceSHans Rosenfeld goto out;
444a61ed2ceSHans Rosenfeld }
445a61ed2ceSHans Rosenfeld
446118b2dbfSJoshua M. Clulow if ((ubuf = pcsc_mem_alloc(hdl, ulen)) == NULL) {
447a61ed2ceSHans Rosenfeld ret = SCARD_E_NO_MEMORY;
448a61ed2ceSHans Rosenfeld goto out;
449a61ed2ceSHans Rosenfeld }
450a61ed2ceSHans Rosenfeld
451a61ed2ceSHans Rosenfeld bufpp = (void *)bufp;
452a61ed2ceSHans Rosenfeld *bufpp = ubuf;
453a61ed2ceSHans Rosenfeld }
454a61ed2ceSHans Rosenfeld ret = SCARD_S_SUCCESS;
455a61ed2ceSHans Rosenfeld
456a61ed2ceSHans Rosenfeld for (off = 0, i = 0; i < npaths; i++) {
457a61ed2ceSHans Rosenfeld size_t slen = strlen(readers[i]) + 1;
458a61ed2ceSHans Rosenfeld bcopy(readers[i], ubuf + off, slen);
459a61ed2ceSHans Rosenfeld off += slen;
460a61ed2ceSHans Rosenfeld VERIFY3U(off, <=, len);
461a61ed2ceSHans Rosenfeld }
462a61ed2ceSHans Rosenfeld VERIFY3U(off, ==, len - 1);
463a61ed2ceSHans Rosenfeld ubuf[off] = '\0';
464a61ed2ceSHans Rosenfeld out:
465a61ed2ceSHans Rosenfeld for (i = 0; i < npaths; i++) {
466a61ed2ceSHans Rosenfeld free(readers[i]);
467a61ed2ceSHans Rosenfeld }
468a61ed2ceSHans Rosenfeld free(readers);
469a61ed2ceSHans Rosenfeld (void) fts_close(fts);
470a61ed2ceSHans Rosenfeld return (ret);
471a61ed2ceSHans Rosenfeld }
472a61ed2ceSHans Rosenfeld
473a61ed2ceSHans Rosenfeld static LONG
uccid_status_helper(int fd,DWORD prots,uccid_cmd_status_t * ucs)474a61ed2ceSHans Rosenfeld uccid_status_helper(int fd, DWORD prots, uccid_cmd_status_t *ucs)
475a61ed2ceSHans Rosenfeld {
476a61ed2ceSHans Rosenfeld /*
477a61ed2ceSHans Rosenfeld * Get the status of this slot and find out information about the slot.
478a61ed2ceSHans Rosenfeld * We need to see if there's an ICC present and if it matches the
479a61ed2ceSHans Rosenfeld * current protocol. If not, then we have to fail this.
480a61ed2ceSHans Rosenfeld */
481a61ed2ceSHans Rosenfeld bzero(ucs, sizeof (uccid_cmd_status_t));
482a61ed2ceSHans Rosenfeld ucs->ucs_version = UCCID_CURRENT_VERSION;
483a61ed2ceSHans Rosenfeld if (ioctl(fd, UCCID_CMD_STATUS, ucs) != 0) {
484a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
485a61ed2ceSHans Rosenfeld }
486a61ed2ceSHans Rosenfeld
487a61ed2ceSHans Rosenfeld if ((ucs->ucs_status & UCCID_STATUS_F_CARD_PRESENT) == 0) {
488a61ed2ceSHans Rosenfeld return (SCARD_W_REMOVED_CARD);
489a61ed2ceSHans Rosenfeld }
490a61ed2ceSHans Rosenfeld
491a61ed2ceSHans Rosenfeld if ((ucs->ucs_status & UCCID_STATUS_F_CARD_ACTIVE) == 0) {
492a61ed2ceSHans Rosenfeld return (SCARD_W_UNPOWERED_CARD);
493a61ed2ceSHans Rosenfeld }
494a61ed2ceSHans Rosenfeld
495a61ed2ceSHans Rosenfeld if ((ucs->ucs_status & UCCID_STATUS_F_PARAMS_VALID) == 0) {
496a61ed2ceSHans Rosenfeld return (SCARD_W_UNSUPPORTED_CARD);
497a61ed2ceSHans Rosenfeld }
498a61ed2ceSHans Rosenfeld
499a61ed2ceSHans Rosenfeld if ((ucs->ucs_prot & prots) == 0) {
500a61ed2ceSHans Rosenfeld return (SCARD_E_PROTO_MISMATCH);
501a61ed2ceSHans Rosenfeld }
502a61ed2ceSHans Rosenfeld
503a61ed2ceSHans Rosenfeld return (0);
504a61ed2ceSHans Rosenfeld }
505a61ed2ceSHans Rosenfeld
506a61ed2ceSHans Rosenfeld LONG
SCardConnect(SCARDCONTEXT hdl,LPCSTR reader,DWORD mode,DWORD prots,LPSCARDHANDLE iccp,LPDWORD protp)507a61ed2ceSHans Rosenfeld SCardConnect(SCARDCONTEXT hdl, LPCSTR reader, DWORD mode, DWORD prots,
508a61ed2ceSHans Rosenfeld LPSCARDHANDLE iccp, LPDWORD protp)
509a61ed2ceSHans Rosenfeld {
510a61ed2ceSHans Rosenfeld LONG ret;
511a61ed2ceSHans Rosenfeld uccid_cmd_status_t ucs;
512a61ed2ceSHans Rosenfeld pcsc_card_t *card;
513a61ed2ceSHans Rosenfeld
514118b2dbfSJoshua M. Clulow if (!pcsc_valid_context(hdl)) {
515118b2dbfSJoshua M. Clulow return (SCARD_E_INVALID_HANDLE);
516118b2dbfSJoshua M. Clulow }
517118b2dbfSJoshua M. Clulow
518a61ed2ceSHans Rosenfeld if (reader == NULL) {
519a61ed2ceSHans Rosenfeld return (SCARD_E_UNKNOWN_READER);
520a61ed2ceSHans Rosenfeld }
521a61ed2ceSHans Rosenfeld
522a61ed2ceSHans Rosenfeld if (iccp == NULL || protp == NULL) {
523a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
524a61ed2ceSHans Rosenfeld }
525a61ed2ceSHans Rosenfeld
526a61ed2ceSHans Rosenfeld if (mode != SCARD_SHARE_SHARED) {
527a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
528a61ed2ceSHans Rosenfeld }
529a61ed2ceSHans Rosenfeld
530a61ed2ceSHans Rosenfeld if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
531a61ed2ceSHans Rosenfeld SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
532a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
533a61ed2ceSHans Rosenfeld }
534a61ed2ceSHans Rosenfeld
535a61ed2ceSHans Rosenfeld if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
536a61ed2ceSHans Rosenfeld return (SCARD_E_UNSUPPORTED_FEATURE);
537a61ed2ceSHans Rosenfeld }
538a61ed2ceSHans Rosenfeld
539118b2dbfSJoshua M. Clulow if ((card = pcsc_card_alloc(hdl, reader)) == NULL) {
540118b2dbfSJoshua M. Clulow pcsc_card_free(card);
541a61ed2ceSHans Rosenfeld return (SCARD_E_NO_MEMORY);
542a61ed2ceSHans Rosenfeld }
543a61ed2ceSHans Rosenfeld
544a61ed2ceSHans Rosenfeld if ((card->pcc_fd = open(reader, O_RDWR)) < 0) {
545118b2dbfSJoshua M. Clulow pcsc_card_free(card);
546a61ed2ceSHans Rosenfeld switch (errno) {
547a61ed2ceSHans Rosenfeld case ENOENT:
548a61ed2ceSHans Rosenfeld return (SCARD_E_UNKNOWN_READER);
549a61ed2ceSHans Rosenfeld default:
550a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
551a61ed2ceSHans Rosenfeld }
552a61ed2ceSHans Rosenfeld }
553a61ed2ceSHans Rosenfeld
554118b2dbfSJoshua M. Clulow if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0) {
555118b2dbfSJoshua M. Clulow pcsc_card_free(card);
556118b2dbfSJoshua M. Clulow return (ret);
557118b2dbfSJoshua M. Clulow }
558a61ed2ceSHans Rosenfeld
559a61ed2ceSHans Rosenfeld *protp = ucs.ucs_prot;
560a61ed2ceSHans Rosenfeld *iccp = card;
561a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
562118b2dbfSJoshua M. Clulow }
563118b2dbfSJoshua M. Clulow
564118b2dbfSJoshua M. Clulow /*
565118b2dbfSJoshua M. Clulow * The Windows documentation suggests that all of the input/output arguments
566118b2dbfSJoshua M. Clulow * (other than the handle) are effectively optional.
567118b2dbfSJoshua M. Clulow */
568118b2dbfSJoshua M. Clulow LONG
SCardStatus(SCARDHANDLE arg,LPSTR readerp,LPDWORD readerlenp,LPDWORD statep,LPDWORD protop,LPBYTE atrp,LPDWORD atrlenp)569118b2dbfSJoshua M. Clulow SCardStatus(SCARDHANDLE arg, LPSTR readerp, LPDWORD readerlenp,
570118b2dbfSJoshua M. Clulow LPDWORD statep, LPDWORD protop, LPBYTE atrp, LPDWORD atrlenp)
571118b2dbfSJoshua M. Clulow {
572118b2dbfSJoshua M. Clulow pcsc_card_t *card = arg;
573118b2dbfSJoshua M. Clulow pcsc_hdl_t *hdl = card->pcc_hdl;
574118b2dbfSJoshua M. Clulow LONG ret = SCARD_S_SUCCESS;
575118b2dbfSJoshua M. Clulow
576118b2dbfSJoshua M. Clulow if (statep == NULL && protop == NULL && atrlenp == NULL) {
577118b2dbfSJoshua M. Clulow /*
578118b2dbfSJoshua M. Clulow * There is no need to perform the status ioctl.
579118b2dbfSJoshua M. Clulow */
580118b2dbfSJoshua M. Clulow goto name;
581118b2dbfSJoshua M. Clulow }
582118b2dbfSJoshua M. Clulow
583118b2dbfSJoshua M. Clulow uccid_cmd_status_t ucs = { .ucs_version = UCCID_CURRENT_VERSION };
584118b2dbfSJoshua M. Clulow if (ioctl(card->pcc_fd, UCCID_CMD_STATUS, &ucs) != 0) {
585118b2dbfSJoshua M. Clulow VERIFY3S(errno, ==, ENODEV);
586118b2dbfSJoshua M. Clulow ret = SCARD_E_READER_UNAVAILABLE;
587118b2dbfSJoshua M. Clulow goto out;
588118b2dbfSJoshua M. Clulow }
589118b2dbfSJoshua M. Clulow
590118b2dbfSJoshua M. Clulow if (statep != NULL) {
591118b2dbfSJoshua M. Clulow if (!(ucs.ucs_status & UCCID_STATUS_F_CARD_PRESENT)) {
592118b2dbfSJoshua M. Clulow *statep = SCARD_ABSENT;
593118b2dbfSJoshua M. Clulow } else if (ucs.ucs_status & UCCID_STATUS_F_CARD_ACTIVE) {
594118b2dbfSJoshua M. Clulow if (ucs.ucs_status & UCCID_STATUS_F_PARAMS_VALID) {
595118b2dbfSJoshua M. Clulow *statep = SCARD_SPECIFIC;
596118b2dbfSJoshua M. Clulow } else {
597118b2dbfSJoshua M. Clulow *statep = SCARD_POWERED;
598118b2dbfSJoshua M. Clulow }
599118b2dbfSJoshua M. Clulow } else {
600118b2dbfSJoshua M. Clulow *statep = SCARD_PRESENT;
601118b2dbfSJoshua M. Clulow }
602118b2dbfSJoshua M. Clulow }
603118b2dbfSJoshua M. Clulow
604118b2dbfSJoshua M. Clulow if (protop != NULL) {
605118b2dbfSJoshua M. Clulow if (ucs.ucs_status & UCCID_STATUS_F_PARAMS_VALID) {
606118b2dbfSJoshua M. Clulow switch (ucs.ucs_prot) {
607118b2dbfSJoshua M. Clulow case UCCID_PROT_T0:
608118b2dbfSJoshua M. Clulow *protop = SCARD_PROTOCOL_T0;
609118b2dbfSJoshua M. Clulow break;
610118b2dbfSJoshua M. Clulow case UCCID_PROT_T1:
611118b2dbfSJoshua M. Clulow *protop = SCARD_PROTOCOL_T1;
612118b2dbfSJoshua M. Clulow break;
613118b2dbfSJoshua M. Clulow default:
614118b2dbfSJoshua M. Clulow *protop = SCARD_PROTOCOL_UNDEFINED;
615118b2dbfSJoshua M. Clulow break;
616118b2dbfSJoshua M. Clulow }
617118b2dbfSJoshua M. Clulow } else {
618118b2dbfSJoshua M. Clulow /*
619118b2dbfSJoshua M. Clulow * If SCARD_SPECIFIC is not returned as the card
620118b2dbfSJoshua M. Clulow * state, this value is not considered meaningful.
621118b2dbfSJoshua M. Clulow */
622118b2dbfSJoshua M. Clulow *protop = SCARD_PROTOCOL_UNDEFINED;
623118b2dbfSJoshua M. Clulow }
624118b2dbfSJoshua M. Clulow }
625118b2dbfSJoshua M. Clulow
626118b2dbfSJoshua M. Clulow if (atrlenp != NULL) {
627118b2dbfSJoshua M. Clulow uint8_t *ubuf;
628118b2dbfSJoshua M. Clulow uint32_t len = *atrlenp;
629118b2dbfSJoshua M. Clulow if (len != SCARD_AUTOALLOCATE) {
630118b2dbfSJoshua M. Clulow if (len < ucs.ucs_atrlen) {
631118b2dbfSJoshua M. Clulow *atrlenp = ucs.ucs_atrlen;
632118b2dbfSJoshua M. Clulow ret = SCARD_E_INSUFFICIENT_BUFFER;
633118b2dbfSJoshua M. Clulow goto out;
634118b2dbfSJoshua M. Clulow }
635118b2dbfSJoshua M. Clulow
636118b2dbfSJoshua M. Clulow if (atrp == NULL) {
637118b2dbfSJoshua M. Clulow ret = SCARD_E_INVALID_PARAMETER;
638118b2dbfSJoshua M. Clulow goto out;
639118b2dbfSJoshua M. Clulow }
640118b2dbfSJoshua M. Clulow
641118b2dbfSJoshua M. Clulow ubuf = atrp;
642118b2dbfSJoshua M. Clulow } else {
643118b2dbfSJoshua M. Clulow if ((ubuf = pcsc_mem_alloc(hdl, ucs.ucs_atrlen)) ==
644118b2dbfSJoshua M. Clulow NULL) {
645118b2dbfSJoshua M. Clulow ret = SCARD_E_NO_MEMORY;
646118b2dbfSJoshua M. Clulow goto out;
647118b2dbfSJoshua M. Clulow }
648118b2dbfSJoshua M. Clulow
649118b2dbfSJoshua M. Clulow *((LPBYTE *)atrp) = ubuf;
650118b2dbfSJoshua M. Clulow }
651118b2dbfSJoshua M. Clulow
652118b2dbfSJoshua M. Clulow bcopy(ucs.ucs_atr, ubuf, ucs.ucs_atrlen);
653118b2dbfSJoshua M. Clulow *atrlenp = ucs.ucs_atrlen;
654118b2dbfSJoshua M. Clulow }
655118b2dbfSJoshua M. Clulow
656118b2dbfSJoshua M. Clulow name:
657118b2dbfSJoshua M. Clulow if (readerlenp != NULL) {
658118b2dbfSJoshua M. Clulow char *ubuf;
659118b2dbfSJoshua M. Clulow uint32_t rlen = *readerlenp;
660118b2dbfSJoshua M. Clulow if (rlen != SCARD_AUTOALLOCATE) {
661118b2dbfSJoshua M. Clulow if (rlen < card->pcc_namelen) {
662118b2dbfSJoshua M. Clulow *readerlenp = card->pcc_namelen;
663118b2dbfSJoshua M. Clulow ret = SCARD_E_INSUFFICIENT_BUFFER;
664118b2dbfSJoshua M. Clulow goto out;
665118b2dbfSJoshua M. Clulow }
666118b2dbfSJoshua M. Clulow
667118b2dbfSJoshua M. Clulow if (readerp == NULL) {
668118b2dbfSJoshua M. Clulow ret = SCARD_E_INVALID_PARAMETER;
669118b2dbfSJoshua M. Clulow goto out;
670118b2dbfSJoshua M. Clulow }
671118b2dbfSJoshua M. Clulow
672118b2dbfSJoshua M. Clulow ubuf = readerp;
673118b2dbfSJoshua M. Clulow } else {
674118b2dbfSJoshua M. Clulow if ((ubuf = pcsc_mem_alloc(hdl, card->pcc_namelen)) ==
675118b2dbfSJoshua M. Clulow NULL) {
676118b2dbfSJoshua M. Clulow ret = SCARD_E_NO_MEMORY;
677118b2dbfSJoshua M. Clulow goto out;
678118b2dbfSJoshua M. Clulow }
679118b2dbfSJoshua M. Clulow
680118b2dbfSJoshua M. Clulow *((LPSTR *)readerp) = ubuf;
681118b2dbfSJoshua M. Clulow }
682118b2dbfSJoshua M. Clulow
683118b2dbfSJoshua M. Clulow /*
684118b2dbfSJoshua M. Clulow * We stored the reader name as a multi-string in
685118b2dbfSJoshua M. Clulow * pcsc_card_alloc(), so we can just copy out the whole value
686118b2dbfSJoshua M. Clulow * here without further modification:
687118b2dbfSJoshua M. Clulow */
688118b2dbfSJoshua M. Clulow bcopy(card->pcc_name, ubuf, card->pcc_namelen);
689118b2dbfSJoshua M. Clulow }
690118b2dbfSJoshua M. Clulow
691118b2dbfSJoshua M. Clulow out:
692a61ed2ceSHans Rosenfeld return (ret);
693a61ed2ceSHans Rosenfeld }
694a61ed2ceSHans Rosenfeld
695a61ed2ceSHans Rosenfeld LONG
SCardDisconnect(SCARDHANDLE arg,DWORD disposition)696a61ed2ceSHans Rosenfeld SCardDisconnect(SCARDHANDLE arg, DWORD disposition)
697a61ed2ceSHans Rosenfeld {
698a61ed2ceSHans Rosenfeld pcsc_card_t *card = arg;
699a61ed2ceSHans Rosenfeld
700a61ed2ceSHans Rosenfeld if (arg == NULL) {
701a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_HANDLE);
702a61ed2ceSHans Rosenfeld }
703a61ed2ceSHans Rosenfeld
704118b2dbfSJoshua M. Clulow switch (disposition) {
705118b2dbfSJoshua M. Clulow case SCARD_RESET_CARD: {
706118b2dbfSJoshua M. Clulow /*
707118b2dbfSJoshua M. Clulow * To reset the card, we first need to get exclusive access to
708118b2dbfSJoshua M. Clulow * the card.
709118b2dbfSJoshua M. Clulow */
710118b2dbfSJoshua M. Clulow uccid_cmd_txn_begin_t txnbegin = {
711118b2dbfSJoshua M. Clulow .uct_version = UCCID_CURRENT_VERSION,
712118b2dbfSJoshua M. Clulow };
713118b2dbfSJoshua M. Clulow if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txnbegin) != 0) {
714118b2dbfSJoshua M. Clulow VERIFY3S(errno, !=, EFAULT);
715118b2dbfSJoshua M. Clulow
716118b2dbfSJoshua M. Clulow switch (errno) {
717118b2dbfSJoshua M. Clulow case ENODEV:
718118b2dbfSJoshua M. Clulow /*
719118b2dbfSJoshua M. Clulow * If the card is no longer present, we cannot
720118b2dbfSJoshua M. Clulow * reset it.
721118b2dbfSJoshua M. Clulow */
722118b2dbfSJoshua M. Clulow goto close;
723118b2dbfSJoshua M. Clulow case EEXIST:
724118b2dbfSJoshua M. Clulow break;
725118b2dbfSJoshua M. Clulow case EBUSY:
726118b2dbfSJoshua M. Clulow return (SCARD_E_SHARING_VIOLATION);
727118b2dbfSJoshua M. Clulow default:
728118b2dbfSJoshua M. Clulow return (SCARD_F_UNKNOWN_ERROR);
729118b2dbfSJoshua M. Clulow }
730118b2dbfSJoshua M. Clulow }
731118b2dbfSJoshua M. Clulow
732118b2dbfSJoshua M. Clulow /*
733118b2dbfSJoshua M. Clulow * Once we have begun the transaction, we can end it
734118b2dbfSJoshua M. Clulow * immediately while requesting a reset before the next
735118b2dbfSJoshua M. Clulow * transaction.
736118b2dbfSJoshua M. Clulow */
737118b2dbfSJoshua M. Clulow uccid_cmd_txn_end_t txnend = {
738118b2dbfSJoshua M. Clulow .uct_version = UCCID_CURRENT_VERSION,
739118b2dbfSJoshua M. Clulow .uct_flags = UCCID_TXN_END_RESET,
740118b2dbfSJoshua M. Clulow };
741118b2dbfSJoshua M. Clulow if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txnend) != 0) {
742118b2dbfSJoshua M. Clulow VERIFY3S(errno, !=, EFAULT);
743118b2dbfSJoshua M. Clulow
744118b2dbfSJoshua M. Clulow switch (errno) {
745118b2dbfSJoshua M. Clulow case ENODEV:
746118b2dbfSJoshua M. Clulow goto close;
747118b2dbfSJoshua M. Clulow default:
748118b2dbfSJoshua M. Clulow return (SCARD_F_UNKNOWN_ERROR);
749118b2dbfSJoshua M. Clulow }
750118b2dbfSJoshua M. Clulow }
751118b2dbfSJoshua M. Clulow }
752118b2dbfSJoshua M. Clulow case SCARD_LEAVE_CARD:
753118b2dbfSJoshua M. Clulow break;
754118b2dbfSJoshua M. Clulow default:
755a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
756a61ed2ceSHans Rosenfeld }
757a61ed2ceSHans Rosenfeld
758118b2dbfSJoshua M. Clulow close:
759a61ed2ceSHans Rosenfeld if (close(card->pcc_fd) != 0) {
760a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
761a61ed2ceSHans Rosenfeld }
762118b2dbfSJoshua M. Clulow card->pcc_fd = -1;
763a61ed2ceSHans Rosenfeld
764118b2dbfSJoshua M. Clulow pcsc_card_free(card);
765a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
766a61ed2ceSHans Rosenfeld }
767a61ed2ceSHans Rosenfeld
768a61ed2ceSHans Rosenfeld LONG
SCardBeginTransaction(SCARDHANDLE arg)769a61ed2ceSHans Rosenfeld SCardBeginTransaction(SCARDHANDLE arg)
770a61ed2ceSHans Rosenfeld {
771a61ed2ceSHans Rosenfeld uccid_cmd_txn_begin_t txn;
772a61ed2ceSHans Rosenfeld pcsc_card_t *card = arg;
773a61ed2ceSHans Rosenfeld
774a61ed2ceSHans Rosenfeld if (card == NULL) {
775a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_HANDLE);
776a61ed2ceSHans Rosenfeld }
777a61ed2ceSHans Rosenfeld
778a61ed2ceSHans Rosenfeld /*
779*cd934150SJoshua M. Clulow * According to the Windows documentation, this function "waits for the
780*cd934150SJoshua M. Clulow * completion of all other transactions before it begins". Software in
781*cd934150SJoshua M. Clulow * the wild has been observed to be confused by the
782*cd934150SJoshua M. Clulow * SCARD_E_SHARING_VIOLATION condition we would previously return if
783*cd934150SJoshua M. Clulow * the card was already in use. As a compatibility measure, we omit
784*cd934150SJoshua M. Clulow * the UCCID_TXN_DONT_BLOCK flag so that the kernel will sleep until
785*cd934150SJoshua M. Clulow * the card is no longer busy before returning.
786a61ed2ceSHans Rosenfeld */
787a61ed2ceSHans Rosenfeld bzero(&txn, sizeof (uccid_cmd_txn_begin_t));
788a61ed2ceSHans Rosenfeld txn.uct_version = UCCID_CURRENT_VERSION;
789a61ed2ceSHans Rosenfeld
790a61ed2ceSHans Rosenfeld if (ioctl(card->pcc_fd, UCCID_CMD_TXN_BEGIN, &txn) != 0) {
791a61ed2ceSHans Rosenfeld VERIFY3S(errno, !=, EFAULT);
792a61ed2ceSHans Rosenfeld switch (errno) {
793a61ed2ceSHans Rosenfeld case ENODEV:
794a61ed2ceSHans Rosenfeld return (SCARD_E_READER_UNAVAILABLE);
795a61ed2ceSHans Rosenfeld case EEXIST:
796a61ed2ceSHans Rosenfeld /*
797a61ed2ceSHans Rosenfeld * This is an odd case. It's used to tell us that we
798a61ed2ceSHans Rosenfeld * already have it. For now, just treat it as success.
799a61ed2ceSHans Rosenfeld */
800a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
801a61ed2ceSHans Rosenfeld case EBUSY:
802a61ed2ceSHans Rosenfeld return (SCARD_E_SHARING_VIOLATION);
803a61ed2ceSHans Rosenfeld /*
804a61ed2ceSHans Rosenfeld * EINPROGRESS is a weird case. It means that we were trying to
805a61ed2ceSHans Rosenfeld * grab a hold while another instance using the same handle was.
806a61ed2ceSHans Rosenfeld * For now, treat it as an unknown error.
807a61ed2ceSHans Rosenfeld */
808a61ed2ceSHans Rosenfeld case EINPROGRESS:
809a61ed2ceSHans Rosenfeld case EINTR:
810a61ed2ceSHans Rosenfeld default:
811a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
812a61ed2ceSHans Rosenfeld }
813a61ed2ceSHans Rosenfeld }
814a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
815a61ed2ceSHans Rosenfeld }
816a61ed2ceSHans Rosenfeld
817a61ed2ceSHans Rosenfeld LONG
SCardEndTransaction(SCARDHANDLE arg,DWORD state)818a61ed2ceSHans Rosenfeld SCardEndTransaction(SCARDHANDLE arg, DWORD state)
819a61ed2ceSHans Rosenfeld {
820a61ed2ceSHans Rosenfeld uccid_cmd_txn_end_t txn;
821a61ed2ceSHans Rosenfeld pcsc_card_t *card = arg;
822a61ed2ceSHans Rosenfeld
823a61ed2ceSHans Rosenfeld if (card == NULL) {
824a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_HANDLE);
825a61ed2ceSHans Rosenfeld }
826a61ed2ceSHans Rosenfeld
827a61ed2ceSHans Rosenfeld bzero(&txn, sizeof (uccid_cmd_txn_end_t));
828a61ed2ceSHans Rosenfeld txn.uct_version = UCCID_CURRENT_VERSION;
829a61ed2ceSHans Rosenfeld
830a61ed2ceSHans Rosenfeld switch (state) {
831a61ed2ceSHans Rosenfeld case SCARD_LEAVE_CARD:
832a61ed2ceSHans Rosenfeld txn.uct_flags = UCCID_TXN_END_RELEASE;
833a61ed2ceSHans Rosenfeld break;
834a61ed2ceSHans Rosenfeld case SCARD_RESET_CARD:
835a61ed2ceSHans Rosenfeld txn.uct_flags = UCCID_TXN_END_RESET;
836a61ed2ceSHans Rosenfeld break;
837a61ed2ceSHans Rosenfeld case SCARD_UNPOWER_CARD:
838a61ed2ceSHans Rosenfeld case SCARD_EJECT_CARD:
839a61ed2ceSHans Rosenfeld default:
840a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
841a61ed2ceSHans Rosenfeld }
842a61ed2ceSHans Rosenfeld
843a61ed2ceSHans Rosenfeld if (ioctl(card->pcc_fd, UCCID_CMD_TXN_END, &txn) != 0) {
844a61ed2ceSHans Rosenfeld VERIFY3S(errno, !=, EFAULT);
845a61ed2ceSHans Rosenfeld switch (errno) {
846a61ed2ceSHans Rosenfeld case ENODEV:
847a61ed2ceSHans Rosenfeld return (SCARD_E_READER_UNAVAILABLE);
848a61ed2ceSHans Rosenfeld case ENXIO:
849a61ed2ceSHans Rosenfeld return (SCARD_E_SHARING_VIOLATION);
850a61ed2ceSHans Rosenfeld default:
851a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
852a61ed2ceSHans Rosenfeld }
853a61ed2ceSHans Rosenfeld }
854a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
855a61ed2ceSHans Rosenfeld }
856a61ed2ceSHans Rosenfeld
857a61ed2ceSHans Rosenfeld LONG
SCardReconnect(SCARDHANDLE arg,DWORD mode,DWORD prots,DWORD init,LPDWORD protp)858a61ed2ceSHans Rosenfeld SCardReconnect(SCARDHANDLE arg, DWORD mode, DWORD prots, DWORD init,
859a61ed2ceSHans Rosenfeld LPDWORD protp)
860a61ed2ceSHans Rosenfeld {
861a61ed2ceSHans Rosenfeld uccid_cmd_status_t ucs;
862a61ed2ceSHans Rosenfeld pcsc_card_t *card = arg;
863a61ed2ceSHans Rosenfeld LONG ret;
864a61ed2ceSHans Rosenfeld
865a61ed2ceSHans Rosenfeld if (card == NULL) {
866a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_HANDLE);
867a61ed2ceSHans Rosenfeld }
868a61ed2ceSHans Rosenfeld
869a61ed2ceSHans Rosenfeld if (protp == NULL) {
870a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
871a61ed2ceSHans Rosenfeld }
872a61ed2ceSHans Rosenfeld
873a61ed2ceSHans Rosenfeld if (mode != SCARD_SHARE_SHARED) {
874a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
875a61ed2ceSHans Rosenfeld }
876a61ed2ceSHans Rosenfeld
877a61ed2ceSHans Rosenfeld if ((prots & ~(SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 |
878a61ed2ceSHans Rosenfeld SCARD_PROTOCOL_RAW | SCARD_PROTOCOL_T15)) != 0) {
879a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
880a61ed2ceSHans Rosenfeld }
881a61ed2ceSHans Rosenfeld
882a61ed2ceSHans Rosenfeld if ((prots & (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)) == 0) {
883a61ed2ceSHans Rosenfeld return (SCARD_E_UNSUPPORTED_FEATURE);
884a61ed2ceSHans Rosenfeld }
885a61ed2ceSHans Rosenfeld
886a61ed2ceSHans Rosenfeld if (init != SCARD_LEAVE_CARD) {
887a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_VALUE);
888a61ed2ceSHans Rosenfeld }
889a61ed2ceSHans Rosenfeld
890a61ed2ceSHans Rosenfeld if ((ret = uccid_status_helper(card->pcc_fd, prots, &ucs)) != 0)
891a61ed2ceSHans Rosenfeld return (ret);
892a61ed2ceSHans Rosenfeld
893a61ed2ceSHans Rosenfeld *protp = ucs.ucs_prot;
894a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
895a61ed2ceSHans Rosenfeld }
896a61ed2ceSHans Rosenfeld
897a61ed2ceSHans Rosenfeld LONG
SCardTransmit(SCARDHANDLE arg,const SCARD_IO_REQUEST * sendreq,LPCBYTE sendbuf,DWORD sendlen,SCARD_IO_REQUEST * recvreq,LPBYTE recvbuf,LPDWORD recvlenp)898a61ed2ceSHans Rosenfeld SCardTransmit(SCARDHANDLE arg, const SCARD_IO_REQUEST *sendreq,
899a61ed2ceSHans Rosenfeld LPCBYTE sendbuf, DWORD sendlen, SCARD_IO_REQUEST *recvreq, LPBYTE recvbuf,
900a61ed2ceSHans Rosenfeld LPDWORD recvlenp)
901a61ed2ceSHans Rosenfeld {
902a61ed2ceSHans Rosenfeld int len;
903a61ed2ceSHans Rosenfeld ssize_t ret;
904a61ed2ceSHans Rosenfeld pcsc_card_t *card = arg;
905a61ed2ceSHans Rosenfeld
906a61ed2ceSHans Rosenfeld if (card == NULL) {
907a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_HANDLE);
908a61ed2ceSHans Rosenfeld }
909a61ed2ceSHans Rosenfeld
910a61ed2ceSHans Rosenfeld /*
911a61ed2ceSHans Rosenfeld * Ignore sendreq / recvreq.
912a61ed2ceSHans Rosenfeld */
913a61ed2ceSHans Rosenfeld if (sendbuf == NULL || recvbuf == NULL || recvlenp == NULL) {
914a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
915a61ed2ceSHans Rosenfeld }
916a61ed2ceSHans Rosenfeld
917a61ed2ceSHans Rosenfeld /*
918a61ed2ceSHans Rosenfeld * The CCID write will always consume all data or none.
919a61ed2ceSHans Rosenfeld */
920a61ed2ceSHans Rosenfeld ret = write(card->pcc_fd, sendbuf, sendlen);
921a61ed2ceSHans Rosenfeld if (ret == -1) {
922a61ed2ceSHans Rosenfeld switch (errno) {
923a61ed2ceSHans Rosenfeld case E2BIG:
924a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
925a61ed2ceSHans Rosenfeld case ENODEV:
926a61ed2ceSHans Rosenfeld return (SCARD_E_READER_UNAVAILABLE);
927a61ed2ceSHans Rosenfeld case EACCES:
928a61ed2ceSHans Rosenfeld case EBUSY:
929a61ed2ceSHans Rosenfeld return (SCARD_E_SHARING_VIOLATION);
930a61ed2ceSHans Rosenfeld case ENXIO:
931a61ed2ceSHans Rosenfeld return (SCARD_W_REMOVED_CARD);
932a61ed2ceSHans Rosenfeld case EFAULT:
933a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
934a61ed2ceSHans Rosenfeld case ENOMEM:
935a61ed2ceSHans Rosenfeld default:
936a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
937a61ed2ceSHans Rosenfeld }
938a61ed2ceSHans Rosenfeld }
939a61ed2ceSHans Rosenfeld ASSERT3S(ret, ==, sendlen);
940a61ed2ceSHans Rosenfeld
941a61ed2ceSHans Rosenfeld /*
942a61ed2ceSHans Rosenfeld * Now, we should be able to block in read.
943a61ed2ceSHans Rosenfeld */
944a61ed2ceSHans Rosenfeld ret = read(card->pcc_fd, recvbuf, *recvlenp);
945a61ed2ceSHans Rosenfeld if (ret == -1) {
946a61ed2ceSHans Rosenfeld switch (errno) {
947a61ed2ceSHans Rosenfeld case EINVAL:
948a61ed2ceSHans Rosenfeld case EOVERFLOW:
949a61ed2ceSHans Rosenfeld /*
950a61ed2ceSHans Rosenfeld * This means that we need to update len with the real
951a61ed2ceSHans Rosenfeld * one.
952a61ed2ceSHans Rosenfeld */
953a61ed2ceSHans Rosenfeld if (ioctl(card->pcc_fd, FIONREAD, &len) != 0) {
954a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
955a61ed2ceSHans Rosenfeld }
956a61ed2ceSHans Rosenfeld *recvlenp = len;
957a61ed2ceSHans Rosenfeld return (SCARD_E_INSUFFICIENT_BUFFER);
958a61ed2ceSHans Rosenfeld case ENODEV:
959a61ed2ceSHans Rosenfeld return (SCARD_E_READER_UNAVAILABLE);
960a61ed2ceSHans Rosenfeld case EACCES:
961a61ed2ceSHans Rosenfeld case EBUSY:
962a61ed2ceSHans Rosenfeld return (SCARD_E_SHARING_VIOLATION);
963a61ed2ceSHans Rosenfeld case EFAULT:
964a61ed2ceSHans Rosenfeld return (SCARD_E_INVALID_PARAMETER);
965a61ed2ceSHans Rosenfeld case ENODATA:
966a61ed2ceSHans Rosenfeld default:
967a61ed2ceSHans Rosenfeld return (SCARD_F_UNKNOWN_ERROR);
968a61ed2ceSHans Rosenfeld }
969a61ed2ceSHans Rosenfeld }
970a61ed2ceSHans Rosenfeld
971a61ed2ceSHans Rosenfeld *recvlenp = ret;
972a61ed2ceSHans Rosenfeld
973a61ed2ceSHans Rosenfeld return (SCARD_S_SUCCESS);
974a61ed2ceSHans Rosenfeld }
975