1 /*
2 * Copyright (c) 2006-2007 Zeljko Vrba <zvrba@globalnet.hr>
3 * Copyright (c) 2006-2017 Alon Bar-Lev <alon.barlev@gmail.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * o Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * o Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * o Neither the name of the <ORGANIZATION> nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 @file
33 Main command loop for scdaemon. For compatibility with GnuPG's scdaemon,
34 all command-line options are silently ignored.
35
36 @todo True daemon mode and multi-server mode are not yet implemented. Only
37 one card is currently supported. Client notification of card status change
38 is not implemented.
39 */
40
41 #include "common.h"
42 #include "command.h"
43 #include "dconfig.h"
44 #include <signal.h>
45 #include <getopt.h>
46 #include <errno.h>
47 #include <pkcs11-helper-1.0/pkcs11h-core.h>
48 #if !defined(HAVE_W32_SYSTEM)
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <sys/un.h>
53 #include <sys/select.h>
54 #ifdef HAVE_SYS_UCRED_H
55 #include <sys/ucred.h>
56 #endif
57 #endif
58
59 #if defined(USE_GNUTLS)
60 #include <gnutls/gnutls.h>
61 #endif
62
63 #ifdef HAVE_W32_SYSTEM
64 typedef void *gnupg_fd_t;
65 #define GNUPG_INVALID_FD ((void*)(-1))
66 #define INT2FD(s) ((void *)(s))
67 #define FD2INT(h) ((unsigned int)(h))
68 #else
69 typedef int gnupg_fd_t;
70 #define GNUPG_INVALID_FD (-1)
71 #define INT2FD(s) (s)
72 #define FD2INT(h) (h)
73 #endif
74
75 typedef enum {
76 ACCEPT_THREAD_STOP,
77 ACCEPT_THREAD_CLEAN
78 } accept_command_t;
79
80 struct global_s;
81
82 #if !defined(HAVE_W32_SYSTEM)
83 typedef struct thread_list_s {
84 struct thread_list_s *next;
85 int fd;
86 pthread_t thread;
87 int stopped;
88 struct global_s *global;
89 } *thread_list_t;
90 #endif
91
92 typedef struct global_s {
93 dconfig_data_t config;
94 char *socket_name;
95 #if !defined(HAVE_W32_SYSTEM)
96 thread_list_t *threads;
97 char *socket_dir;
98 int fd_accept_terminate[2];
99 uid_t uid_acl;
100 #endif
101
102 } global_t;
103
104 #if !defined(HAVE_W32_SYSTEM)
105 static int s_parent_pid = -1;
106 #endif
107
108 #define ALARM_INTERVAL 10
109 #define SOCKET_DIR_TEMPLATE ( PACKAGE ".XXXXXX" )
110
111 /** Register commands with assuan. */
112 static
113 int
register_commands(const assuan_context_t ctx)114 register_commands (const assuan_context_t ctx)
115 {
116 static struct {
117 const char *name;
118 assuan_handler_t handler;
119 const char * const help;
120 } table[] = {
121 { "SERIALNO", cmd_serialno, NULL },
122 { "LEARN", cmd_learn, NULL },
123 { "READCERT", cmd_readcert, NULL },
124 { "READKEY", cmd_readkey, NULL },
125 { "KEY-DATA", NULL, NULL },
126 { "SETDATA", cmd_setdata, NULL },
127 { "PKSIGN", cmd_pksign, NULL },
128 { "PKAUTH", NULL, NULL },
129 { "PKDECRYPT", cmd_pkdecrypt, NULL },
130 { "INPUT", NULL, NULL },
131 { "OUTPUT", NULL, NULL },
132 { "GETATTR", cmd_getattr, NULL },
133 { "SETATTR", cmd_setattr, NULL },
134 { "WRITECERT", NULL, NULL },
135 { "WRITEKEY", NULL, NULL },
136 { "GENKEY", cmd_genkey, NULL },
137 { "RANDOM", NULL, NULL },
138 { "PASSWD", NULL, NULL },
139 { "CHECKPIN", cmd_null, NULL },
140 { "LOCK", NULL, NULL },
141 { "UNLOCK", NULL, NULL },
142 { "GETINFO", cmd_getinfo, NULL },
143 { "RESTART", cmd_restart, NULL },
144 { "DISCONNECT", cmd_null, NULL },
145 { "APDU", NULL, NULL },
146 { "CHV-STATUS-1", cmd_null, NULL }, /* gnupg-1.X */
147 { NULL, NULL, NULL }
148 };
149 int i, ret;
150
151 for(i=0; table[i].name; i++) {
152 if (
153 (ret = assuan_register_command (
154 ctx,
155 table[i].name,
156 table[i].handler,
157 table[i].help
158 ))
159 ) {
160 return ret;
161 }
162 }
163
164 assuan_set_hello_line (ctx, "PKCS#11 smart-card server for GnuPG ready");
165 /*assuan_register_reset_notify(ctx, reset_notify);*/
166 /*assuan_register_option_handler(ctx, option_handler);*/
167 return 0;
168 }
169
170 /**
171 Command handler (single-threaded). If fd == -1, this is a pipe server,
172 otherwise fd is UNIX socket fd to which client connected.
173 */
174 static
175 void
command_handler(global_t * global,const int fd)176 command_handler (global_t *global, const int fd)
177 {
178 assuan_context_t ctx = NULL;
179 cmd_data_t data;
180 int ret;
181
182 #if !defined(HAVE_W32_SYSTEM)
183 if (fd != -1 && global->uid_acl != (uid_t)-1) {
184 uid_t peeruid = -1;
185 #if HAVE_DECL_LOCAL_PEERCRED
186 struct xucred xucred;
187 socklen_t len = sizeof(xucred);
188 if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xucred, &len) == -1) {
189 common_log (LOG_WARNING, "Cannot get socket credentials: %s", strerror (errno));
190 goto cleanup;
191 }
192 if (xucred.cr_version != XUCRED_VERSION) {
193 common_log (LOG_WARNING, "Mismatch credentials version actual %d expected %d", xucred.cr_version, XUCRED_VERSION);
194 goto cleanup;
195 }
196 peeruid = xucred.cr_uid;
197 #elif HAVE_DECL_SO_PEERCRED
198 struct ucred ucred;
199 socklen_t len = sizeof(ucred);
200 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
201 common_log (LOG_WARNING, "Cannot get socket credentials: %s", strerror (errno));
202 goto cleanup;
203 }
204 peeruid = ucred.uid;
205 #endif
206 if (peeruid != global->uid_acl) {
207 common_log (LOG_WARNING, "Mismatch credentials actual %d expected %d", peeruid, global->uid_acl);
208 goto cleanup;
209 }
210 }
211 #endif
212
213 memset (&data, 0, sizeof (data));
214 data.config = &global->config;
215 data.socket_name = global->socket_name;
216
217 if ((ret = assuan_new(&ctx)) != 0) {
218 common_log (LOG_ERROR,"failed to create assuan context %s", gpg_strerror (ret));
219 goto cleanup;
220 }
221
222 if(fd < 0) {
223 #if !defined(HAVE_W32_SYSTEM)
224 assuan_fd_t fds[2] = {INT2FD(0), INT2FD(1)};
225 #else
226 assuan_fd_t fds[2] = {GetStdHandle(STD_INPUT_HANDLE), GetStdHandle(STD_OUTPUT_HANDLE)};
227 #endif
228 ret = assuan_init_pipe_server (ctx, fds);
229 } else {
230 ret = assuan_init_socket_server (ctx, INT2FD(fd), ASSUAN_SOCKET_SERVER_ACCEPTED);
231 }
232
233 if (ret != 0) {
234 common_log (LOG_ERROR,"failed to initialize server: %s", gpg_strerror (ret));
235 goto cleanup;
236 }
237
238 if(((ret = register_commands(ctx))) != 0) {
239 common_log (LOG_ERROR,"failed to register assuan commands: %s", gpg_strerror (ret));
240 goto cleanup;
241 }
242
243 if (global->config.verbose) {
244 assuan_set_log_stream (ctx, common_get_log_stream());
245 }
246
247 assuan_set_pointer (ctx, &data);
248
249 while (1) {
250 common_log (LOG_DEBUG, "accepting connection");
251
252 if ((ret = assuan_accept (ctx)) == -1) {
253 break;
254 }
255
256 if (ret != 0) {
257 common_log (LOG_WARNING,"assuan_accept failed: %s", gpg_strerror(ret));
258 break;
259 }
260
261 common_log (LOG_DEBUG, "processing connection");
262
263 if ((ret = assuan_process (ctx)) != 0) {
264 common_log (LOG_WARNING,"assuan_process failed: %s", gpg_strerror(ret));
265 }
266
267 common_log (LOG_DEBUG, "post-processing connection");
268 }
269
270 cleanup:
271
272 common_log (LOG_DEBUG, "cleanup connection");
273
274 if (ctx != NULL) {
275 cmd_free_data (ctx);
276 assuan_release (ctx);
277 ctx = NULL;
278 }
279 }
280
281 #if !defined(HAVE_W32_SYSTEM)
282 static
283 void
server_socket_close(global_t * global,const int fd)284 server_socket_close (global_t *global, const int fd) {
285 if (fd != -1) {
286 assuan_sock_close (fd);
287 }
288 if (global->socket_name != NULL) {
289 unlink (global->socket_name);
290 free (global->socket_name);
291 global->socket_name = NULL;
292 }
293 if (global->socket_dir != NULL) {
294 rmdir (global->socket_dir);
295 free (global->socket_dir);
296 global->socket_dir = NULL;
297 }
298 assuan_sock_deinit();
299 }
300
301 static
302 void
server_socket_create_name(global_t * global)303 server_socket_create_name (global_t *global) {
304
305 char *socketdir = getenv("GNUPG_PKCS11_SOCKETDIR");
306 if (socketdir == NULL) {
307 socketdir = getenv("TMPDIR");
308 }
309 if (socketdir == NULL) {
310 socketdir = "/tmp";
311 }
312
313 if ((global->socket_dir = malloc(strlen(socketdir) + strlen(SOCKET_DIR_TEMPLATE) + 100)) == NULL) {
314 common_log (LOG_FATAL, "malloc");
315 }
316 sprintf(global->socket_dir, "%s/%s", socketdir, SOCKET_DIR_TEMPLATE);
317
318 if (mkdtemp (global->socket_dir) == NULL) {
319 common_log (LOG_FATAL, "Cannot mkdtemp");
320 }
321
322 if ((global->socket_name = (char *)malloc (strlen (global->socket_dir) + 100)) == NULL) {
323 common_log (LOG_FATAL, "Cannot malloc");
324 }
325
326 sprintf (global->socket_name, "%s/agent.S", global->socket_dir);
327
328 }
329
330 static
331 int
server_socket_create(global_t * global)332 server_socket_create (global_t *global) {
333 struct sockaddr_un serv_addr;
334 int fd = -1;
335 int rc = -1;
336
337 if ((rc = assuan_sock_init()) != 0) {
338 common_log (LOG_ERROR,"Cannot init socket %s", gpg_strerror (rc));
339 goto cleanup;
340 }
341
342 memset (&serv_addr, 0, sizeof (serv_addr));
343 serv_addr.sun_family = AF_UNIX;
344 assert (strlen (global->socket_name) + 1 < sizeof (serv_addr.sun_path));
345 strcpy (serv_addr.sun_path, global->socket_name);
346
347 if ((fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0)) == -1) {
348 common_log (LOG_ERROR, "Cannot create socket", global->socket_name);
349 goto cleanup;
350 }
351
352 if ((rc = assuan_sock_bind (fd, (struct sockaddr*)&serv_addr, sizeof (serv_addr))) == -1) {
353 common_log (LOG_ERROR, "Cannot bing to socket '%s'", global->socket_name);
354 goto cleanup;
355 }
356
357 #if !defined(HAVE_W32_SYSTEM)
358 if (global->uid_acl != (uid_t)-1) {
359 if (chmod(global->socket_name, 0666) == -1) {
360 common_log (LOG_ERROR, "Cannot chmod '%s'", global->socket_name);
361 goto cleanup;
362 }
363 if (chmod(global->socket_dir, 0755) == -1) {
364 common_log (LOG_ERROR, "Cannot chmod '%s'", global->socket_dir);
365 goto cleanup;
366 }
367 }
368 #endif
369
370 if ((rc = listen (fd, SOMAXCONN)) == -1) {
371 common_log (LOG_ERROR, "Cannot listen to socket '%s'", global->socket_name);
372 goto cleanup;
373 }
374
375 rc = 0;
376
377 cleanup:
378
379 if (rc != 0) {
380 server_socket_close (global, fd);
381 common_log (LOG_FATAL, "Cannot handle socket");
382 }
383
384 common_log (LOG_INFO, "Listening to socket '%s'", global->socket_name);
385
386 return fd;
387 }
388
389 static
390 void *
_server_socket_command_handler(void * arg)391 _server_socket_command_handler (void *arg) {
392 thread_list_t entry = (thread_list_t)arg;
393 accept_command_t clean = ACCEPT_THREAD_CLEAN;
394
395 command_handler (entry->global, entry->fd);
396 entry->stopped = 1;
397
398 if (write (entry->global->fd_accept_terminate[1], &clean, sizeof (clean)) == -1) {
399 common_log (LOG_FATAL, "write failed");
400 }
401
402 return NULL;
403 }
404
405 static
406 void *
_server_socket_accept(void * arg)407 _server_socket_accept (void *arg) {
408 thread_list_t _entry = (thread_list_t)arg;
409 global_t *global = _entry->global;
410 int fd = _entry->fd;
411 thread_list_t thread_list_head = NULL;
412 int rc = 0;
413
414 free (_entry);
415 _entry = NULL;
416
417 if (pipe (global->fd_accept_terminate) == -1) {
418 common_log (LOG_FATAL, "pipe failed");
419 }
420
421 while (rc != -1) {
422 fd_set fdset;
423
424 FD_ZERO (&fdset);
425 FD_SET (global->fd_accept_terminate[0], &fdset);
426 FD_SET (fd, &fdset);
427
428 rc = select (FD_SETSIZE, &fdset, NULL, NULL, NULL);
429
430 if (rc != -1 && rc != 0) {
431 if (FD_ISSET (global->fd_accept_terminate[0], &fdset)) {
432 accept_command_t cmd;
433
434 if (
435 (rc = read (
436 global->fd_accept_terminate[0],
437 &cmd,
438 sizeof (cmd))
439 ) == sizeof (cmd)
440 ) {
441 if (cmd == ACCEPT_THREAD_STOP) {
442 common_log (LOG_DEBUG, "Thread command terminate");
443 rc = -1;
444 }
445 else if (cmd == ACCEPT_THREAD_CLEAN) {
446 thread_list_t entry = thread_list_head;
447 thread_list_t prev = NULL;
448
449 common_log (LOG_DEBUG, "Cleaning up closed thread");
450 while (entry != NULL) {
451 if (entry->stopped) {
452 thread_list_t temp = entry;
453
454 common_log (LOG_DEBUG, "Cleaning up closed thread1");
455 pthread_join (entry->thread, NULL);
456 close (entry->fd);
457
458 if (prev == NULL) {
459 thread_list_head = entry->next;
460 }
461 else {
462 prev->next = entry->next;
463 }
464
465 entry = entry->next;
466
467 free (temp);
468 }
469 else {
470 prev = entry;
471 entry = entry->next;
472 }
473 }
474 }
475 }
476 }
477 else if (FD_ISSET (fd, &fdset)) {
478 struct sockaddr_un addr;
479 socklen_t addrlen = sizeof (addr);
480 int fd2;
481
482 if ((rc = fd2 = accept (fd, (struct sockaddr *)&addr, &addrlen)) != -1) {
483 thread_list_t entry = NULL;
484
485 common_log (LOG_DEBUG, "Accepted new socket connection");
486
487 if ((entry = (thread_list_t)malloc (sizeof (struct thread_list_s))) == NULL) {
488 common_log (LOG_FATAL, "malloc failed");
489 }
490 memset (entry, 0, sizeof (struct thread_list_s));
491 entry->next = thread_list_head;
492 entry->fd = fd2;
493 entry->global = global;
494 thread_list_head = entry;
495
496 if (
497 pthread_create (
498 &entry->thread,
499 NULL,
500 _server_socket_command_handler,
501 entry
502 )
503 ) {
504 common_log (LOG_FATAL, "pthread failed");
505 }
506
507 }
508 }
509 }
510 }
511
512 common_log (LOG_DEBUG, "Cleaning up threads");
513 while (thread_list_head != NULL) {
514 thread_list_t entry = thread_list_head;
515 thread_list_head = thread_list_head->next;
516 common_log (LOG_DEBUG, "Cleaning up thread1");
517 close (entry->fd);
518 pthread_join (entry->thread, NULL);
519 free (entry);
520 }
521
522 return NULL;
523 }
524
525 static
526 void
server_socket_accept(global_t * global,const int fd,pthread_t * thread)527 server_socket_accept (global_t *global, const int fd, pthread_t *thread) {
528 thread_list_t entry = malloc (sizeof (struct thread_list_s));
529 memset (entry, 0, sizeof (struct thread_list_s));
530 entry->fd = fd;
531 entry->global = global;
532 if (pthread_create (thread, NULL, _server_socket_accept, (void *)entry)) {
533 common_log (LOG_FATAL, "pthread failed");
534 }
535 }
536
537 static
538 void
server_socket_accept_terminate(global_t * global,pthread_t thread)539 server_socket_accept_terminate (global_t *global, pthread_t thread) {
540 accept_command_t stop = ACCEPT_THREAD_STOP;
541 if (write (global->fd_accept_terminate[1], &stop, sizeof (stop)) == -1) {
542 common_log (LOG_FATAL, "write failed");
543 }
544 pthread_join (thread, NULL);
545 close (global->fd_accept_terminate[0]);
546 close (global->fd_accept_terminate[1]);
547 }
548 #endif /* HAVE_W32_SYSTEM */
549
550 static
551 void
pkcs11_log_hook(void * const data,const unsigned flags,const char * const fmt,va_list args)552 pkcs11_log_hook (
553 void * const data,
554 const unsigned flags,
555 const char * const fmt,
556 va_list args
557 ) {
558 (void)data;
559 (void)flags;
560
561 common_vlog (LOG_INFO, fmt, args);
562 }
563
564 static
565 PKCS11H_BOOL
pkcs11_token_prompt_hook(void * const global_data,void * const user_data,const pkcs11h_token_id_t token,const unsigned retry)566 pkcs11_token_prompt_hook (
567 void * const global_data,
568 void * const user_data,
569 const pkcs11h_token_id_t token,
570 const unsigned retry
571 ) {
572 char cmd[1024];
573 unsigned char *user_read = NULL;
574 size_t user_read_len = 0;
575 assuan_context_t ctx = user_data;
576 int rc;
577 int ret = FALSE;
578
579 (void)global_data;
580 (void)retry;
581
582 snprintf (
583 cmd,
584 sizeof(cmd),
585 "NEEDPIN Please insert token '%s' !!!DO NOT ENTER PIN HERE!!!!",
586 token->display
587 );
588
589 if ((rc = assuan_inquire (ctx, cmd, &user_read, &user_read_len, 1024))) {
590 common_log (LOG_WARNING, "Token inquire error: %d", rc);
591 goto cleanup;
592 }
593
594 if (!strcmp ((char *)user_read, "cancel")) {
595 goto cleanup;
596 }
597
598 ret = TRUE;
599
600 cleanup:
601
602 if (user_read != NULL) {
603 memset (user_read, 0, strlen ((char *)user_read));
604 free (user_read);
605 user_read = NULL;
606 }
607
608 return ret;
609 }
610
611 static
612 PKCS11H_BOOL
pkcs11_pin_prompt_hook(void * const global_data,void * const user_data,const pkcs11h_token_id_t token,const unsigned retry,char * const pin,const size_t max_pin)613 pkcs11_pin_prompt_hook (
614 void * const global_data,
615 void * const user_data,
616 const pkcs11h_token_id_t token,
617 const unsigned retry,
618 char * const pin,
619 const size_t max_pin
620 ) {
621 char cmd[1024];
622 assuan_context_t ctx = user_data;
623 unsigned char *pin_read = NULL;
624 size_t pin_len;
625 int rc;
626 int ret = FALSE;
627
628 (void)global_data;
629
630 snprintf (
631 cmd,
632 sizeof(cmd),
633 "NEEDPIN PIN required for token '%s' (try %u)",
634 token->display,
635 retry
636 );
637
638 if ((rc = assuan_inquire (ctx, cmd, &pin_read, &pin_len, 1024))) {
639 common_log (LOG_WARNING,"PIN inquire error: %d", rc);
640 goto cleanup;
641 }
642
643 if (pin_len==0 || (pin_len+1 > max_pin)) {
644 goto cleanup;
645 }
646
647 strcpy (pin, (char *)pin_read);
648
649 ret = TRUE;
650
651 cleanup:
652
653 if (pin_read != NULL) {
654 memset (pin_read, 0, strlen ((char *)pin_read));
655 free (pin_read);
656 pin_read = NULL;
657 }
658
659 return ret;
660 }
661
662 #if !defined(HAVE_W32_SYSTEM)
on_alarm(int signo)663 static RETSIGTYPE on_alarm (int signo)
664 {
665 (void)signo;
666
667 if (s_parent_pid != -1 && kill (s_parent_pid, 0) == -1) {
668 kill (getpid (), SIGTERM);
669 }
670
671 signal (SIGALRM, on_alarm);
672 alarm (ALARM_INTERVAL);
673
674 #if RETSIGTYPE != void
675 return 0
676 #endif
677 }
678
679 static RETSIGTYPE on_signal (int signo)
680 {
681 (void)signo;
682
683 /*
684 * This is the only way to notify
685 * assuan to return from its main loop...
686 */
687 close (0);
688 close (1);
689
690 #if RETSIGTYPE != void
691 return 0
692 #endif
693 }
694 #endif /* HAVE_W32_SYSTEM */
695
696 static void usage (const char * const argv0)
697 {
698
699 printf (
700 (
701 "%s %s\n"
702 "\n"
703 "Copyright (c) 2006-2007 Zeljko Vrba <zvrba@globalnet.hr>\n"
704 "Copyright (c) 2006-2017 Alon Bar-Lev <alon.barlev@gmail.com>\n"
705 "This program comes with ABSOLUTELY NO WARRANTY.\n"
706 "This is free software, and you are welcome to redistribute it\n"
707 "under certain conditions. See the file COPYING for details.\n"
708 "\n"
709 "Syntax: %s [options]\n"
710 "Smartcard daemon for GnuPG\n"
711 "\n"
712 "Options:\n"
713 " \n"
714 " --server run in server mode (foreground)\n"
715 " --multi-server run in multi server mode (foreground)\n"
716 " --daemon run in daemon mode (background)\n"
717 " -v, --verbose verbose\n"
718 " -q, --quiet be somewhat more quiet\n"
719 " -s, --sh sh-style command output\n"
720 " -c, --csh csh-style command output\n"
721 " --options read options from file\n"
722 " --no-detach do not detach from the console\n"
723 " --homedir specify home directory\n"
724 #if !defined(HAVE_W32_SYSTEM)
725 " --uid-acl accept only this uid, implies world read/write socket\n"
726 #endif
727 " --log-file use a log file for the server\n"
728 " --help print this information\n"
729 ),
730 PACKAGE,
731 PACKAGE_VERSION,
732 argv0
733 );
734 }
735
736 static char *get_home_dir (void) {
737 #if defined(HAVE_W32_SYSTEM)
738 static const char * GPG_HOME_KEY = "Software\\GNU\\GnuPG";
739 const char *HOME_ENV = getenv ("USERPROFILE");
740 #else
741 const char *HOME_ENV = getenv ("HOME");
742 #endif
743 char *home_dir = NULL;
744
745 if (home_dir == NULL && getenv ("GNUPGHOME") != NULL) {
746 home_dir=strdup (getenv ("GNUPGHOME"));
747 }
748 #if defined(HAVE_W32_SYSTEM)
749 if (home_dir == NULL) {
750 char key_val[1024];
751 HKEY hkey = NULL;
752 DWORD dw = 0;
753
754 if (RegOpenKeyEx (HKEY_CURRENT_USER, GPG_HOME_KEY, 0, KEY_READ, &hkey) != ERROR_SUCCESS) {
755 if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, GPG_HOME_KEY, 0, KEY_READ, &hkey) != ERROR_SUCCESS) {
756 hkey = NULL;
757 }
758 }
759 if (hkey != NULL) {
760 if (
761 RegQueryValueEx (
762 hkey,
763 "HomeDir",
764 NULL,
765 NULL,
766 (PBYTE)key_val,
767 &dw
768 ) == ERROR_SUCCESS
769 ) {
770 home_dir = strdup (key_val);
771 }
772 }
773 if (hkey != NULL) {
774 RegCloseKey (hkey);
775 }
776
777 }
778 #endif
779
780 if (home_dir == NULL) {
781 if (
782 CONFIG_GPG_HOME[0] == '~' &&
783 HOME_ENV != NULL
784 ) {
785 if ((home_dir=(char *)malloc (strlen (CONFIG_GPG_HOME) + strlen (HOME_ENV))) == NULL) {
786 common_log (LOG_FATAL, "malloc failed");
787 }
788 sprintf (home_dir, "%s%s", HOME_ENV, CONFIG_GPG_HOME+1);
789 }
790 else {
791 home_dir = strdup (CONFIG_GPG_HOME);
792 }
793 }
794
795 return home_dir;
796 }
797
798 int main (int argc, char *argv[])
799 {
800 enum {
801 OPT_SERVER,
802 OPT_MUTLI_SERVER,
803 OPT_DAEMON,
804 OPT_VERBOSE,
805 OPT_QUIET,
806 OPT_SH,
807 OPT_CSH,
808 OPT_OPTIONS,
809 OPT_NO_DETACH,
810 OPT_HOMEDIR,
811 #if !defined(HAVE_W32_SYSTEM)
812 OPT_UID_ACL,
813 #endif
814 OPT_LOG_FILE,
815 OPT_VERSION,
816 OPT_HELP
817 };
818
819 static struct option long_options[] = {
820 { "server", no_argument, NULL, OPT_SERVER },
821 { "multi-server", no_argument, NULL, OPT_MUTLI_SERVER },
822 { "daemon", no_argument, NULL, OPT_DAEMON },
823 { "verbose", no_argument, NULL, OPT_VERBOSE },
824 { "quiet", no_argument, NULL, OPT_QUIET },
825 { "sh", no_argument, NULL, OPT_SH },
826 { "csh", no_argument, NULL, OPT_CSH },
827 { "options", required_argument, NULL, OPT_OPTIONS },
828 { "no-detach", no_argument, NULL, OPT_NO_DETACH },
829 { "homedir", required_argument, NULL, OPT_HOMEDIR },
830 #if !defined(HAVE_W32_SYSTEM)
831 { "uid-acl", required_argument, NULL, OPT_UID_ACL },
832 #endif
833 { "log-file", required_argument, NULL, OPT_LOG_FILE },
834 { "version", no_argument, NULL, OPT_VERSION },
835 { "help", no_argument, NULL, OPT_HELP },
836 { NULL, 0, NULL, 0 }
837 };
838 int opt;
839 enum {
840 RUN_MODE_NONE,
841 RUN_MODE_SERVER,
842 RUN_MODE_MULTI_SERVER,
843 RUN_MODE_DAEMON
844 } run_mode = RUN_MODE_NONE;
845 int env_is_csh = 0;
846 int log_verbose = 0;
847 int log_quiet = 0;
848 int no_detach = 0;
849 char *config_file = NULL;
850 char *log_file = NULL;
851 char *home_dir = NULL;
852 int have_at_least_one_provider=0;
853 FILE *fp_log = NULL;
854 int i;
855 CK_RV rv;
856
857 global_t global;
858
859 const char * CONFIG_SUFFIX = ".conf";
860 char *default_config_file = NULL;
861
862 /* unused intentionally */
863 (void)log_quiet;
864
865 memset(&global, 0, sizeof(global));
866
867 #if !defined(HAVE_W32_SYSTEM)
868 s_parent_pid = getpid ();
869 global.fd_accept_terminate[0] = -1;
870 global.fd_accept_terminate[1] = -1;
871 global.uid_acl = (uid_t)-1;
872 #endif
873
874 if ((default_config_file = (char *)malloc (strlen (PACKAGE)+strlen (CONFIG_SUFFIX)+1)) == NULL) {
875 common_log (LOG_FATAL, "malloc failed");
876 }
877 sprintf (default_config_file, "%s%s", PACKAGE, CONFIG_SUFFIX);
878
879 common_set_log_stream (stderr);
880
881 while ((opt = getopt_long (argc, argv, "vqsc", long_options, NULL)) != -1) {
882 switch (opt) {
883 case OPT_SERVER:
884 run_mode = RUN_MODE_SERVER;
885 break;
886 case OPT_MUTLI_SERVER:
887 run_mode = RUN_MODE_MULTI_SERVER;
888 break;
889 case OPT_DAEMON:
890 run_mode = RUN_MODE_DAEMON;
891 break;
892 case OPT_VERBOSE:
893 case 'v':
894 log_verbose = 1;
895 break;
896 case OPT_QUIET:
897 case 'q':
898 log_quiet = 1;
899 break;
900 case OPT_SH:
901 case 's':
902 break;
903 case OPT_CSH:
904 case 'c':
905 env_is_csh = 1;
906 break;
907 case OPT_OPTIONS:
908 config_file = strdup (optarg);
909 break;
910 case OPT_NO_DETACH:
911 no_detach = 1;
912 break;
913 case OPT_HOMEDIR:
914 home_dir = strdup (optarg);
915 break;
916 #if !defined(HAVE_W32_SYSTEM)
917 case OPT_UID_ACL:
918 global.uid_acl = atoi(optarg);
919 break;
920 #endif
921 case OPT_LOG_FILE:
922 log_file = optarg;
923 break;
924 case OPT_VERSION:
925 printf (
926 "%s %s\n"
927 "\n"
928 "Copyright (c) 2006-2007 Zeljko Vrba <zvrba@globalnet.hr>\n"
929 "Copyright (c) 2006-2017 Alon Bar-Lev <alon.barlev@gmail.com>\n"
930 "\n"
931 "This is free software; see the source for copying conditions.\n"
932 "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
933 PACKAGE,
934 PACKAGE_VERSION
935 );
936 exit (0);
937 break;
938 case OPT_HELP:
939 usage(argv[0]);
940 exit(0);
941 break;
942 default:
943 fprintf(stderr, "Invalid usage\n");
944 exit(1);
945 break;
946 }
947 }
948
949 if (run_mode == RUN_MODE_NONE) {
950 common_log (LOG_FATAL, "please use the option `--daemon' to run the program in the background");
951 }
952
953 #if defined(HAVE_W32_SYSTEM)
954 if (run_mode == RUN_MODE_DAEMON) {
955 common_log (LOG_FATAL, "daemon mode is not supported");
956 }
957 #endif
958
959 if (home_dir == NULL) {
960 home_dir = get_home_dir ();
961 }
962
963 if (config_file == NULL) {
964 if ((config_file = (char *)malloc (strlen (home_dir) + strlen (default_config_file)+2)) == NULL) {
965 common_log (LOG_FATAL, "malloc failed");
966 }
967 sprintf (config_file, "%s%c%s", home_dir, CONFIG_PATH_SEPARATOR, default_config_file);
968 }
969
970 if (
971 !dconfig_read (config_file, &global.config) &&
972 !dconfig_read (CONFIG_SYSTEM_CONFIG, &global.config)
973 ) {
974 common_log (LOG_FATAL, "Cannot open configuration file");
975 }
976
977 if (log_verbose) {
978 global.config.verbose = 1;
979 }
980
981 #if !defined(HAVE_W32_SYSTEM)
982 signal (SIGPIPE, SIG_IGN);
983 {
984 struct sigaction action;
985 memset(&action, 0, sizeof(action));
986 action.sa_handler = on_signal;
987 sigaction(SIGINT, &action, NULL);
988 sigaction(SIGTERM, &action, NULL);
989 sigaction(SIGABRT, &action, NULL);
990 sigaction(SIGHUP, &action, NULL);
991 }
992 #endif
993
994 if (log_file == NULL) {
995 log_file = global.config.log_file;
996 }
997
998 if (log_file != NULL) {
999 if (strcmp (log_file, "stderr") != 0) {
1000 if ((fp_log = fopen (log_file, "a")) != NULL) {
1001 #if !defined(HAVE_W32_SYSTEM)
1002 fchmod(fileno(fp_log), 0600);
1003 #else
1004 _chmod(log_file, 0600);
1005 #endif
1006 common_set_log_stream (fp_log);
1007 }
1008 }
1009 }
1010
1011 if (global.config.debug) {
1012 common_log (LOG_DEBUG, "version: %s", PACKAGE_VERSION);
1013 dconfig_print (&global.config);
1014 common_log (LOG_DEBUG, "run_mode: %d", run_mode);
1015 common_log (LOG_DEBUG, "crypto: %s",
1016 #if defined(ENABLE_OPENSSL)
1017 "openssl"
1018 #elif defined(ENABLE_GNUTLS)
1019 "gnutls"
1020 #else
1021 "invalid"
1022 #endif
1023 );
1024 }
1025
1026 if (!gcry_check_version (GCRYPT_VERSION)) {
1027 common_log (LOG_FATAL, "Cannot initialize libcrypt");
1028 }
1029 gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
1030 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
1031
1032 #if !defined(HAVE_W32_SYSTEM)
1033 if (run_mode == RUN_MODE_DAEMON || run_mode == RUN_MODE_MULTI_SERVER) {
1034 server_socket_create_name (&global);
1035 }
1036
1037 /*
1038 * fork before doing PKCS#11 stuff
1039 * some providers don't behave well
1040 */
1041 if (run_mode == RUN_MODE_DAEMON) {
1042 pid_t pid;
1043
1044 pid = fork ();
1045
1046 if (pid == -1) {
1047 common_log (LOG_FATAL, "fork failed");
1048 }
1049
1050 if (pid != 0) {
1051 static const char *key = "SCDAEMON_INFO";
1052 char env[1024];
1053 snprintf (env, sizeof (env), "%s:%lu:1", global.socket_name, (unsigned long)pid);
1054
1055 if (optind < argc) {
1056 setenv(key, env, 1);
1057 execvp (argv[optind], &(argv[optind]));
1058 kill (pid, SIGTERM);
1059 exit (1);
1060 }
1061 else {
1062 if (env_is_csh) {
1063 *strchr (env, '=') = ' ';
1064 printf ("setenv %s %s\n", key, env);
1065 }
1066 else {
1067 printf ("%s=%s; export %s\n", key, env, key);
1068 }
1069 exit (0);
1070 }
1071 }
1072
1073 if (!no_detach) {
1074 int i;
1075
1076 for (i=0;i<3;i++) {
1077 if (fileno (common_get_log_stream ()) != i) {
1078 close (i);
1079 }
1080 }
1081
1082 if (setsid () == -1) {
1083 common_log (LOG_FATAL, "setsid failed");
1084 }
1085 }
1086
1087 if (chdir ("/") == -1) {
1088 common_log (LOG_FATAL, "chdir failed");
1089 }
1090
1091 if (optind < argc) {
1092 struct sigaction sa;
1093
1094 memset (&sa, 0, sizeof (sa));
1095 sigemptyset (&sa.sa_mask);
1096 #if defined(SA_INTERRUPT)
1097 sa.sa_flags |= SA_INTERRUPT;
1098 #endif
1099 sa.sa_handler = on_alarm;
1100 sigaction (SIGALRM, &sa, NULL);
1101 alarm (10);
1102 }
1103 }
1104 #endif /* HAVE_W32_SYSTEM */
1105
1106 assuan_set_assuan_log_prefix (PACKAGE);
1107 assuan_set_assuan_log_stream (common_get_log_stream ());
1108
1109 #if defined(USE_GNUTLS)
1110 if (gnutls_global_init () != GNUTLS_E_SUCCESS) {
1111 common_log (LOG_FATAL, "Cannot initialize gnutls");
1112 }
1113 #endif
1114
1115 if ((rv = pkcs11h_initialize ()) != CKR_OK) {
1116 common_log (LOG_FATAL, "Cannot initialize PKCS#11: %s", pkcs11h_getMessage (rv));
1117 }
1118
1119 pkcs11h_setLogLevel (global.config.verbose ? PKCS11H_LOG_DEBUG2 : PKCS11H_LOG_INFO);
1120 pkcs11h_setLogHook (pkcs11_log_hook, NULL);
1121 pkcs11h_setTokenPromptHook (pkcs11_token_prompt_hook, NULL);
1122 pkcs11h_setPINPromptHook (pkcs11_pin_prompt_hook, NULL);
1123 pkcs11h_setProtectedAuthentication (TRUE);
1124 pkcs11h_setPINCachePeriod(global.config.pin_cache);
1125
1126 for (i=0;i<DCONFIG_MAX_PROVIDERS;i++) {
1127 if (
1128 global.config.providers[i].name != NULL &&
1129 global.config.providers[i].library != NULL
1130 ) {
1131 if (
1132 (rv = pkcs11h_addProvider (
1133 global.config.providers[i].name,
1134 global.config.providers[i].library,
1135 global.config.providers[i].allow_protected,
1136 global.config.providers[i].private_mask,
1137 PKCS11H_SLOTEVENT_METHOD_POLL,
1138 0,
1139 global.config.providers[i].cert_is_private
1140 )) != CKR_OK
1141 ) {
1142 common_log (LOG_WARNING, "Cannot add PKCS#11 provider '%s': %ld-'%s'", global.config.providers[i].name, rv, pkcs11h_getMessage (rv));
1143 }
1144 else {
1145 have_at_least_one_provider = 1;
1146 }
1147 }
1148 }
1149
1150 if (!have_at_least_one_provider) {
1151 common_log (LOG_FATAL, "Could not load any provider");
1152 }
1153
1154 #if defined(HAVE_W32_SYSTEM)
1155 command_handler (&global, -1);
1156 #else
1157 {
1158 pthread_t accept_thread = 0;
1159 int accept_socket = -1;
1160
1161 if (run_mode == RUN_MODE_DAEMON || run_mode == RUN_MODE_MULTI_SERVER) {
1162 accept_socket = server_socket_create (&global);
1163
1164 server_socket_accept (&global, accept_socket, &accept_thread);
1165 }
1166
1167 if (run_mode == RUN_MODE_DAEMON) {
1168 /*
1169 * Emulate assuan behavior
1170 */
1171 int fds[2];
1172 char c;
1173 if (pipe (fds)==-1) {
1174 common_log (LOG_FATAL, "Could not create pipe");
1175 }
1176 close (0);
1177 dup2 (fds[0], 0);
1178 close (fds[0]);
1179 while (read (0, &c, 1) == -1 && errno == EINTR);
1180 close (fds[1]);
1181 }
1182 else {
1183 command_handler (&global, -1);
1184 }
1185
1186 common_log (LOG_DEBUG, "Terminating");
1187
1188 if (run_mode == RUN_MODE_DAEMON || run_mode == RUN_MODE_MULTI_SERVER) {
1189 server_socket_accept_terminate (&global, accept_thread);
1190 server_socket_close (&global, accept_socket);
1191 }
1192 }
1193 #endif
1194
1195 pkcs11h_terminate ();
1196
1197 #if defined(USE_GNUTLS)
1198 gnutls_global_deinit ();
1199 #endif
1200
1201 dconfig_free (&global.config);
1202
1203 if (config_file != NULL) {
1204 free (config_file);
1205 config_file = NULL;
1206 }
1207
1208 if (default_config_file != NULL) {
1209 free (default_config_file);
1210 default_config_file = NULL;
1211 }
1212
1213 if (home_dir != NULL) {
1214 free (home_dir);
1215 home_dir = NULL;
1216 }
1217
1218 if (fp_log != NULL) {
1219 fclose (fp_log);
1220 fp_log = NULL;
1221 }
1222
1223 return 0;
1224 }
1225
1226