1 /*
2  * COPYRIGHT (c) International Business Machines Corp. 2001-2017
3  *
4  * This program is provided under the terms of the Common Public License,
5  * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
6  * software constitutes recipient's acceptance of CPL-1.0 terms which can be
7  * found in the file LICENSE file or at
8  * https://opensource.org/licenses/cpl1.0.php
9  */
10 
11 //
12 //
13 //AIX Pkcs11 Api Utility functions
14 //
15 
16 #include <stdint.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <strings.h>
21 #include <unistd.h>
22 #include <dlfcn.h>
23 #include <errno.h>
24 #include <sys/syslog.h>
25 #include <limits.h>
26 
27 #include <sys/ipc.h>
28 
29 #include <pkcs11types.h>
30 #include <apiclient.h>          // Function prototypes for PKCS11
31 #include <slotmgr.h>
32 #include <apictl.h>
33 #include <apiproto.h>
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 
39 static int xplfd = -1;
40 
41 #include <libgen.h>
42 
43 #define LIBLOCATION  LIB_PATH
44 
45 extern API_Proc_Struct_t *Anchor;
46 
47 #include <stdarg.h>
48 #include "trace.h"
49 #include "ock_syslog.h"
50 
CreateProcLock(void)51 CK_RV CreateProcLock(void)
52 {
53     struct stat statbuf;
54 
55     if (xplfd == -1) {
56 
57         /* The slot mgr daemon should have already created lock,
58          * so just open it so we can get a lock...
59          */
60         if (stat(OCK_API_LOCK_FILE, &statbuf) == 0)
61             xplfd = open(OCK_API_LOCK_FILE, O_RDONLY);
62 
63         if (xplfd == -1) {
64             OCK_SYSLOG(LOG_ERR, "Could not open %s\n", OCK_API_LOCK_FILE);
65             return CKR_FUNCTION_FAILED;
66         }
67     }
68 
69     return CKR_OK;
70 }
71 
ProcLock(void)72 CK_RV ProcLock(void)
73 {
74     if (xplfd != -1)
75         flock(xplfd, LOCK_EX);
76     else
77         TRACE_DEVEL("No file descriptor to lock with.\n");
78 
79     return CKR_OK;
80 }
81 
ProcUnLock(void)82 CK_RV ProcUnLock(void)
83 {
84     if (xplfd != -1)
85         flock(xplfd, LOCK_UN);
86     else
87         TRACE_DEVEL("No file descriptor to unlock with.\n");
88 
89     return CKR_OK;
90 }
91 
ProcClose(void)92 CK_RV ProcClose(void)
93 {
94     if (xplfd != -1)
95         close(xplfd);
96     else
97         TRACE_DEVEL("ProcClose: No file descriptor open to close.\n");
98 
99     return CKR_OK;
100 }
101 
AddToSessionList(ST_SESSION_T * pSess)102 unsigned long AddToSessionList(ST_SESSION_T *pSess)
103 {
104     unsigned long handle;
105 
106     handle = bt_node_add(&(Anchor->sess_btree), pSess);
107 
108     return handle;
109 }
110 
RemoveFromSessionList(CK_SESSION_HANDLE handle)111 void RemoveFromSessionList(CK_SESSION_HANDLE handle)
112 {
113     bt_node_free(&(Anchor->sess_btree), handle, free);
114 }
115 
116 /* CloseMe
117  *
118  * Callback function used to close an individual session for a slot
119  */
CloseMe(STDLL_TokData_t * tokdata,void * node_value,unsigned long node_handle,void * arg)120 void CloseMe(STDLL_TokData_t *tokdata, void *node_value,
121              unsigned long node_handle, void *arg)
122 {
123     CK_RV rv;
124     CK_SLOT_ID slot_id = *(CK_SLOT_ID *) arg;
125     ST_SESSION_T *s = (ST_SESSION_T *) node_value;
126     API_Slot_t *sltp;
127     STDLL_FcnList_t *fcn;
128 
129     UNUSED(tokdata);
130 
131     if (s->slotID == slot_id) {
132         /* the single ugliest part about moving to a binary tree: these are the
133          * guts of the C_CloseSession function, copied here without tests for
134          * validity, since if we're here, they must already have been valid */
135         sltp = &(Anchor->SltList[slot_id]);
136         fcn = sltp->FcnList;
137         rv = fcn->ST_CloseSession(sltp->TokData, s);
138         if (rv == CKR_OK) {
139             decr_sess_counts(slot_id);
140             bt_node_free(&(Anchor->sess_btree), node_handle, free);
141         }
142     }
143 }
144 
145 /* CloseAllSessions
146  *
147  * Run through all the nodes in the binary tree and call CloseMe on each one.
148  * CloseMe will look at @slot_id and if it matches, will close the session.
149  * Once all the nodes are closed, we check to see if the tree is empty and if
150  * so, destroy it
151  */
CloseAllSessions(CK_SLOT_ID slot_id)152 void CloseAllSessions(CK_SLOT_ID slot_id)
153 {
154     API_Slot_t *sltp = &(Anchor->SltList[slot_id]);
155 
156     /* for every node in the API-level session tree, call CloseMe on it */
157     bt_for_each_node(sltp->TokData, &(Anchor->sess_btree), CloseMe,
158                      (void *) &slot_id);
159 
160     if (bt_is_empty(&(Anchor->sess_btree)))
161         bt_destroy(&(Anchor->sess_btree), NULL);
162 }
163 
Valid_Session(CK_SESSION_HANDLE handle,ST_SESSION_T * rSession)164 int Valid_Session(CK_SESSION_HANDLE handle, ST_SESSION_T *rSession)
165 {
166     ST_SESSION_T *tmp;
167 
168     tmp = bt_get_node_value(&(Anchor->sess_btree), handle);
169     if (tmp) {
170         rSession->slotID = tmp->slotID;
171         rSession->sessionh = tmp->sessionh;
172     }
173 
174     return (tmp ? TRUE : FALSE);
175 }
176 
API_Initialized()177 int API_Initialized()
178 {
179     if (Anchor == NULL)
180         return FALSE;
181 
182     return TRUE;
183 }
184 
slot_present(CK_SLOT_ID id)185 int slot_present(CK_SLOT_ID id)
186 {
187     Slot_Mgr_Socket_t *shData = &(Anchor->SocketDataP);
188 #ifdef PKCS64
189     Slot_Info_t_64 *sinfp;
190 #else
191     Slot_Info_t *sinfp;
192 #endif
193 
194     sinfp = &(shData->slot_info[id]);
195     if (sinfp->present == FALSE) {
196         return FALSE;
197     }
198 
199     return TRUE;
200 }
201 
get_sess_count(CK_SLOT_ID slotID,CK_ULONG * ret)202 void get_sess_count(CK_SLOT_ID slotID, CK_ULONG *ret)
203 {
204     Slot_Mgr_Shr_t *shm;
205 
206     shm = Anchor->SharedMemP;
207     ProcLock();
208     *ret = shm->slot_global_sessions[slotID];
209     ProcUnLock();
210 }
211 
incr_sess_counts(CK_SLOT_ID slotID)212 void incr_sess_counts(CK_SLOT_ID slotID)
213 {
214     Slot_Mgr_Shr_t *shm;
215 #ifdef PKCS64
216     Slot_Mgr_Proc_t_64 *procp;
217 #else
218     Slot_Mgr_Proc_t *procp;
219 #endif
220 
221     // Get the slot mutex
222     shm = Anchor->SharedMemP;
223 
224     ProcLock();
225 
226     shm->slot_global_sessions[slotID]++;
227 
228     procp = &shm->proc_table[Anchor->MgrProcIndex];
229     procp->slot_session_count[slotID]++;
230 
231     ProcUnLock();
232 }
233 
decr_sess_counts(CK_SLOT_ID slotID)234 void decr_sess_counts(CK_SLOT_ID slotID)
235 {
236     Slot_Mgr_Shr_t *shm;
237 #ifdef PKCS64
238     Slot_Mgr_Proc_t_64 *procp;
239 #else
240     Slot_Mgr_Proc_t *procp;
241 #endif
242 
243     // Get the slot mutex
244     shm = Anchor->SharedMemP;
245 
246     ProcLock();
247 
248     if (shm->slot_global_sessions[slotID] > 0) {
249         shm->slot_global_sessions[slotID]--;
250     }
251 
252     procp = &shm->proc_table[Anchor->MgrProcIndex];
253     if (procp->slot_session_count[slotID] > 0) {
254         procp->slot_session_count[slotID]++;
255     }
256 
257     ProcUnLock();
258 }
259 
260 // Check if any sessions from other applicaitons exist on this particular
261 // token.... This will also validate our own sessions as well.
262 // There might be an issue with the fact that a session is created but the
263 // number is not incremented until the session allocation is completed by
264 // the token.  The API may need to lock the shared memory prior to creating
265 // the session and then unlock when the stdll has completed its work.
266 // Closing sessions should probably behave the same way.
sessions_exist(CK_SLOT_ID slotID)267 int sessions_exist(CK_SLOT_ID slotID)
268 {
269     Slot_Mgr_Shr_t *shm;
270     uint32 numSessions;
271 
272     // Get the slot mutex
273     shm = Anchor->SharedMemP;
274 
275     ProcLock();
276     numSessions = shm->slot_global_sessions[slotID];
277     ProcUnLock();
278 
279     return numSessions != 0;
280 }
281 
282 // Terminates all sessions associated with a given process
283 // this cleans up any lingering sessions with the process
284 // and does not
285 //
286 // It is only called from the C_Finalize routine
Terminate_All_Process_Sessions()287 void Terminate_All_Process_Sessions()
288 {
289     CK_SLOT_ID id;
290     CK_RV rv;
291 
292     TRACE_DEBUG("Terminate_All_Process_Sessions\n");
293     for (id = 0; id < NUMBER_SLOTS_MANAGED; id++) {
294         // Check if the slot is present in the slot manager
295         // if not just skip it...
296         if (slot_present(id) == TRUE) {
297             rv = C_CloseAllSessions(id);
298         } else {
299             continue;
300         }
301         // If the return code is not OK, we are really hosed
302         // since we are terminating the session.
303         // For now we will just log it
304         if (rv != CKR_OK) {
305             TRACE_DEBUG("Terminate_All_Process_Sessions RV %lx\n", rv);
306         }
307     }
308 }
309 
310 // Register the process with PKCSSLOTD in the shared memory.
311 // This call must be made with the API Global Mutex Locked
312 // and the Anchor control block initialized with the
313 // shared memory.  No checking for shared memory validity is done
API_Register()314 int API_Register()
315 {
316     long int reuse = -1, free = -1;
317     Slot_Mgr_Shr_t *shm;
318 
319 #ifdef PKCS64
320     Slot_Mgr_Proc_t_64 *procp;
321 #else
322     Slot_Mgr_Proc_t *procp;
323 #endif
324 
325     uint16 indx;
326 
327     // Grab the Shared Memory lock to prevent other updates to the
328     // SHM Process
329     // The registration is done to allow for future handling of
330     // the Slot Event List.  Which is maintained by the Slotd.
331 
332     shm = Anchor->SharedMemP;
333 
334     ProcLock();
335 
336     procp = shm->proc_table;
337     for (indx = 0; indx < NUMBER_PROCESSES_ALLOWED; indx++, procp++) {
338         // Is the entry in use
339 
340         if (procp->inuse == TRUE) {
341             // Handle the weird case of the process terminating without
342             // un-registering, and restarting with exactly the same PID
343             // before the slot manager garbage collection can performed.
344             // To eliminate the race condition between garbage collection
345             // the lock should protect us.
346             // This should be a VERY rare (if ever) occurance, given the
347             // way AIX deals with re-allocation of PID;s, however if this
348             // ever gets ported over to another platform we want to deal
349             // with this accordingly since it may re-use pids differently
350             // (Linux appears to re-use pids more rapidly).
351             if (procp->proc_id == getpid()) {
352                 if (reuse == -1) {
353                     reuse = indx;
354                 }
355             }
356         } else {
357             //Already found the first free
358             if (free == -1) {
359                 free = indx;
360             }
361         }
362     }
363 
364     // If we did not find a free entry then we fail the routine
365     if ((reuse == -1) && (free == -1)) {
366         ProcUnLock();
367         return FALSE;
368     }
369     // check if we are reusing a control block or taking the first free.
370     // Since th mutex is helt, we don;t have to worry about some other
371     // process grabbing the slot...  Garbage collection from
372     // the slotd should not affect this since it will grab the mutex
373     // before doing its thing.
374     if (reuse != -1) {
375         procp = &(shm->proc_table[reuse]);
376         indx = reuse;
377     } else {
378         procp = &(shm->proc_table[free]);
379         indx = free;
380     }
381 
382 #ifdef PKCS64
383     memset((char *) procp, 0, sizeof(Slot_Mgr_Proc_t_64));
384 #else
385     memset((char *) procp, 0, sizeof(Slot_Mgr_Proc_t));
386 #endif
387     procp->inuse = TRUE;
388     procp->proc_id = getpid();
389     procp->reg_time = time(NULL);
390 
391     Anchor->MgrProcIndex = indx;
392 
393     TRACE_DEVEL("API_Register MgrProcIndc %d  pid %ld \n", procp->proc_id,
394                 (long int) Anchor->MgrProcIndex);
395 
396     //??? What to do about the Mutex and cond variable
397     //Does initializing them in the slotd allow for them to not be
398     //initialized in the application.
399 
400     ProcUnLock();
401 
402     return TRUE;
403 }
404 
405 // DeRegister the process with PKCSSLOTD in the shared memory.
406 // This call must be made with the API Global Mutex Locked
407 // and the Anchor control block initialized with the
408 // shared memory.  No checking for shared memory validity is done
API_UnRegister()409 void API_UnRegister()
410 {
411     Slot_Mgr_Shr_t *shm;
412 
413 #ifdef PKCS64
414     Slot_Mgr_Proc_t_64 *procp;
415 #else
416     Slot_Mgr_Proc_t *procp;
417 #endif
418 
419     // Grab the Shared Memory lock to prevent other updates to the
420     // SHM Process
421     // The registration is done to allow for future handling of
422     // the Slot Event List.  Which is maintained by the Slotd.
423 
424     shm = Anchor->SharedMemP;
425 
426     ProcLock();
427 
428     procp = &(shm->proc_table[Anchor->MgrProcIndex]);
429 
430 #ifdef PKCS64
431     memset((char *) procp, 0, sizeof(Slot_Mgr_Proc_t_64));
432 #else
433     memset((char *) procp, 0, sizeof(Slot_Mgr_Proc_t));
434 #endif
435 
436     Anchor->MgrProcIndex = 0;
437 
438     //??? What to do about the Mutex and cond variable
439     //Does initializing them in the slotd allow for them to not be
440     //initialized in the application.
441 
442     ProcUnLock();
443 }
444 
DL_UnLoad(API_Slot_t * sltp,CK_SLOT_ID slotID)445 void DL_UnLoad(API_Slot_t *sltp, CK_SLOT_ID slotID)
446 {
447     Slot_Mgr_Socket_t *shData = &(Anchor->SocketDataP);
448 #ifdef PKCS64
449     Slot_Info_t_64 *sinfp;
450 #else
451     Slot_Info_t *sinfp;
452 #endif
453 
454     sinfp = &(shData->slot_info[slotID]);
455 
456     if (sinfp->present == FALSE) {
457         return;
458     }
459     if (!sltp->dlop_p) {
460         return;
461     }
462     // Call the routine to properly unload the DLL
463     DL_Unload(sltp);
464 
465     return;
466 }
467 
DL_Loaded(char * location,DLL_Load_t * dllload)468 int DL_Loaded(char *location, DLL_Load_t *dllload)
469 {
470     int i;
471 
472     for (i = 0; i < NUMBER_SLOTS_MANAGED; i++) {
473         if (dllload[i].dll_name != NULL) {
474             TRACE_DEBUG("DL_LOADED Looking for index %d name %s\n",
475                         i, dllload[i].dll_name);
476             if (strcmp(location, dllload[i].dll_name) == 0) {
477                 return i;       // Return the index of the dll
478             }
479         }
480     }
481 
482     return -1;                  // Indicate failure to find the dll
483 }
484 
485 #ifdef PKCS64
DL_Load(Slot_Info_t_64 * sinfp,API_Slot_t * sltp,DLL_Load_t * dllload)486 int DL_Load(Slot_Info_t_64 *sinfp, API_Slot_t *sltp, DLL_Load_t *dllload)
487 #else
488 int DL_Load(Slot_Info_t *sinfp, API_Slot_t *sltp, DLL_Load_t *dllload)
489 #endif
490 {
491     int i;
492 
493     TRACE_DEBUG("DL_LOAD\n");
494     for (i = 0; i < NUMBER_SLOTS_MANAGED; i++) {
495         if (dllload[i].dll_name == NULL) {
496             TRACE_DEBUG("Empty slot at %d \n", i);
497             break;
498         }
499     }
500     if (i == NUMBER_SLOTS_MANAGED) {
501         TRACE_DEBUG("No empty slots.\n");
502         return 0;               // Failed to find it..
503     }
504 
505     dllload[i].dll_name = sinfp->dll_location;  // Point to the location
506 
507     dllload[i].dlop_p = dlopen(sinfp->dll_location, (RTLD_GLOBAL | RTLD_LAZY));
508 
509     if (dllload[i].dlop_p != NULL) {
510         sltp->dlop_p = dllload[i].dlop_p;
511         sltp->dll_information = &dllload[i];
512         dllload[i].dll_load_count++;;
513 
514     } else {
515         char *e = dlerror();
516         OCK_SYSLOG(LOG_WARNING,
517                    "%s: dlopen() failed for [%s]; dlerror = [%s]\n",
518                    __func__, sinfp->dll_location, e);
519         TRACE_DEVEL("DL_Load of %s failed, dlerror: %s\n",
520                     sinfp->dll_location, e);
521         sltp->dlop_p = NULL;
522         return 0;
523     }
524 
525     return 1;
526 }
527 
DL_Unload(API_Slot_t * sltp)528 void DL_Unload(API_Slot_t *sltp)
529 {
530     DLL_Load_t *dllload;
531 
532     // Decrement the count of loads.  When 0 then unload this thing;
533     //
534     dllload = sltp->dll_information;
535     dllload->dll_load_count--;
536     if (dllload->dll_load_count == 0) {
537         dlclose(dllload->dlop_p);
538         dllload->dll_name = NULL;
539     }
540     // Clear out the slot information
541     sltp->DLLoaded = FALSE;
542     sltp->dlop_p = NULL;
543     sltp->pSTfini = NULL;
544     sltp->pSTcloseall = NULL;
545 }
546 
DL_Load_and_Init(API_Slot_t * sltp,CK_SLOT_ID slotID)547 int DL_Load_and_Init(API_Slot_t *sltp, CK_SLOT_ID slotID)
548 {
549     Slot_Mgr_Socket_t *shData = &(Anchor->SocketDataP);
550 #ifdef PKCS64
551     Slot_Info_t_64 *sinfp;
552 #else
553     Slot_Info_t *sinfp;
554 #endif
555 
556     int (*pSTinit) ();
557     void (*pSTfini) ();
558     CK_RV rv;
559     int dll_len, dl_index;
560     DLL_Load_t *dllload;
561 
562     // Get pointer to shared memory from the anchor block
563     //
564 
565     sinfp = &(shData->slot_info[slotID]);
566     dllload = Anchor->DLLs;     // list of dll's in the system
567 
568     if (sinfp->present == FALSE) {
569         return FALSE;
570     }
571 
572     if ((dll_len = strlen(sinfp->dll_location))) {
573         // Check if this DLL has been loaded already.. If so, just increment
574         // the counter in the dllload structure and copy the data to
575         // the slot pointer.
576         if ((dl_index = DL_Loaded(sinfp->dll_location, dllload)) != -1) {
577             dllload[dl_index].dll_load_count++;
578             sltp->dll_information = &dllload[dl_index];
579             sltp->dlop_p = dllload[dl_index].dlop_p;
580         } else {
581             TRACE_DEBUG("DL_Load_and_Init dll_location %s\n",
582                         sinfp->dll_location);
583             DL_Load(sinfp, sltp, dllload);
584         }
585     } else {
586         return FALSE;
587     }
588 
589     if (!sltp->dlop_p) {
590         TRACE_DEBUG("DL_Load_and_Init pointer %p\n", sltp->dlop_p);
591 
592         return FALSE;
593     }
594 
595     *(void **)(&pSTinit) = dlsym(sltp->dlop_p, "ST_Initialize");
596     if (!pSTinit) {
597         // Unload the DLL
598         DL_Unload(sltp);
599         return FALSE;
600     }
601     // Returns true or false
602     rv = pSTinit(sltp, slotID, sinfp, trace);
603     TRACE_DEBUG("return from STDDLL Init = %lx\n", rv);
604 
605     if (rv != CKR_OK) {
606         // clean up and unload
607         DL_Unload(sltp);
608         sltp->DLLoaded = FALSE;
609         return FALSE;
610     } else {
611         sltp->DLLoaded = TRUE;
612         // Check if a SC_Finalize function has been exported
613         *(void **)(&pSTfini) = dlsym(sltp->dlop_p, "SC_Finalize");
614         sltp->pSTfini = pSTfini;
615 
616         *(void **)(&sltp->pSTcloseall) =
617             dlsym(sltp->dlop_p, "SC_CloseAllSessions");
618         return TRUE;
619     }
620 
621     return TRUE;
622 }
623 
624 // copies internal representation of ck_info structure to local process
625 // representation
CK_Info_From_Internal(CK_INFO_PTR dest,CK_INFO_PTR_64 src)626 void CK_Info_From_Internal(CK_INFO_PTR dest, CK_INFO_PTR_64 src)
627 {
628     dest->cryptokiVersion = src->cryptokiVersion;
629 
630     memcpy(dest->manufacturerID, src->manufacturerID,
631            sizeof(dest->manufacturerID));
632 
633     dest->flags = src->flags;
634 
635     memcpy(dest->libraryDescription, src->libraryDescription,
636            sizeof(dest->libraryDescription));
637 
638     dest->libraryVersion = src->libraryVersion;
639 }
640