Coverage Report

Created: 2022-12-08 06:10

/src/libassuan/src/assuan-handler.c
Line
Count
Source (jump to first uncovered line)
1
/* assuan-handler.c - dispatch commands
2
 * Copyright (C) 2001, 2002, 2003, 2007, 2009,
3
 *               2011 Free Software Foundation, Inc.
4
 *
5
 * This file is part of Assuan.
6
 *
7
 * Assuan is free software; you can redistribute it and/or modify it
8
 * under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation; either version 2.1 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * Assuan is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19
 * SPDX-License-Identifier: LGPL-2.1+
20
 */
21
22
#ifdef HAVE_CONFIG_H
23
#include <config.h>
24
#endif
25
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <string.h>
29
#include <errno.h>
30
#if HAVE_W32_SYSTEM || HAVE_W64_SYSTEM
31
#include <fcntl.h>
32
#endif
33
34
#include "assuan-defs.h"
35
#include "debug.h"
36
37
38
0
#define spacep(p)  (*(p) == ' ' || *(p) == '\t')
39
0
#define digitp(a) ((a) >= '0' && (a) <= '9')
40
41
static int my_strcasecmp (const char *a, const char *b);
42
43
44
#define PROCESS_DONE(ctx, rc) \
45
0
  ((ctx)->flags.in_process_next ? assuan_process_done ((ctx), (rc)) : (rc))
46
47
static gpg_error_t
48
dummy_handler (assuan_context_t ctx, char *line)
49
0
{
50
0
  return
51
0
    PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASSUAN_SERVER_FAULT,
52
0
          "no handler registered"));
53
0
}
54
55
56
static const char std_help_nop[] =
57
  "NOP\n"
58
  "\n"
59
  "No operation.  Returns OK without any action.";
60
static gpg_error_t
61
std_handler_nop (assuan_context_t ctx, char *line)
62
0
{
63
0
  return PROCESS_DONE (ctx, 0); /* okay */
64
0
}
65
66
static const char std_help_cancel[] =
67
  "CANCEL\n"
68
  "\n"
69
  "Run the server's cancel handler if one has been registered.";
70
static gpg_error_t
71
std_handler_cancel (assuan_context_t ctx, char *line)
72
0
{
73
0
  if (ctx->cancel_notify_fnc)
74
    /* Return value ignored.  */
75
0
    ctx->cancel_notify_fnc (ctx, line);
76
0
  return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
77
0
}
78
79
static const char std_help_option[] =
80
  "OPTION <NAME> [ [=] <VALUE> ]\n"
81
  "\n"
82
  "Set option <NAME> to configure server operation.  Leading and\n"
83
  "trailing spaces around <NAME> and <VALUE> are allowed but should be\n"
84
  "ignored.  For compatibility reasons, <NAME> may be prefixed with two\n"
85
  "dashes.  The use of the equal sign is optional but suggested if\n"
86
  "<VALUE> is given.";
87
static gpg_error_t
88
std_handler_option (assuan_context_t ctx, char *line)
89
0
{
90
0
  char *key, *value, *p;
91
92
0
  for (key=line; spacep (key); key++)
93
0
    ;
94
0
  if (!*key)
95
0
    return
96
0
      PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required"));
97
0
  if (*key == '=')
98
0
    return
99
0
      PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX,
100
0
            "no option name given"));
101
0
  for (value=key; *value && !spacep (value) && *value != '='; value++)
102
0
    ;
103
0
  if (*value)
104
0
    {
105
0
      if (spacep (value))
106
0
        *value++ = 0; /* terminate key */
107
0
      for (; spacep (value); value++)
108
0
        ;
109
0
      if (*value == '=')
110
0
        {
111
0
          *value++ = 0; /* terminate key */
112
0
          for (; spacep (value); value++)
113
0
            ;
114
0
          if (!*value)
115
0
            return
116
0
        PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX,
117
0
              "option argument expected"));
118
0
        }
119
0
      if (*value)
120
0
        {
121
0
          for (p = value + strlen(value) - 1; p > value && spacep (p); p--)
122
0
            ;
123
0
          if (p > value)
124
0
            *++p = 0; /* strip trailing spaces */
125
0
        }
126
0
    }
127
128
0
  if (*key == '-' && key[1] == '-' && key[2])
129
0
    key += 2; /* the double dashes are optional */
130
0
  if (*key == '-')
131
0
    return PROCESS_DONE (ctx,
132
0
       set_error (ctx, GPG_ERR_ASS_SYNTAX,
133
0
            "option should not begin with one dash"));
134
135
0
  if (ctx->option_handler_fnc)
136
0
    return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value));
137
0
  return PROCESS_DONE (ctx, 0);
138
0
}
139
140
static const char std_help_bye[] =
141
  "BYE\n"
142
  "\n"
143
  "Close the connection.  The server will reply with OK.";
144
static gpg_error_t
145
std_handler_bye (assuan_context_t ctx, char *line)
146
0
{
147
0
  if (ctx->bye_notify_fnc)
148
    /* Return value ignored.  */
149
0
    ctx->bye_notify_fnc (ctx, line);
150
0
  assuan_close_input_fd (ctx);
151
0
  assuan_close_output_fd (ctx);
152
  /* pretty simple :-) */
153
0
  ctx->flags.process_complete = 1;
154
0
  return PROCESS_DONE (ctx, 0);
155
0
}
156
157
static const char std_help_auth[] =
158
  "AUTH\n"
159
  "\n"
160
  "Reserved for future extensions.";
161
static gpg_error_t
162
std_handler_auth (assuan_context_t ctx, char *line)
163
0
{
164
0
  return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
165
0
}
166
167
static const char std_help_reset[] =
168
  "RESET\n"
169
  "\n"
170
  "Reset the connection but not any existing authentication.  The server\n"
171
  "should release all resources associated with the connection.";
172
static gpg_error_t
173
std_handler_reset (assuan_context_t ctx, char *line)
174
0
{
175
0
  gpg_error_t err = 0;
176
177
0
  if (ctx->reset_notify_fnc)
178
0
    err = ctx->reset_notify_fnc (ctx, line);
179
0
  if (! err)
180
0
    {
181
0
      assuan_close_input_fd (ctx);
182
0
      assuan_close_output_fd (ctx);
183
0
      _assuan_uds_close_fds (ctx);
184
0
    }
185
0
  return PROCESS_DONE (ctx, err);
186
0
}
187
188
static const char std_help_help[] =
189
  "HELP [<COMMAND>]\n"
190
  "\n"
191
  "Lists all commands that the server understands as comment lines on\n"
192
  "the status channel.  If <COMMAND> is given, list detailed help for\n"
193
  "that command.";
194
static gpg_error_t
195
std_handler_help (assuan_context_t ctx, char *line)
196
0
{
197
0
  unsigned int i;
198
0
  char buf[ASSUAN_LINELENGTH];
199
0
  const char *helpstr;
200
0
  size_t n;
201
202
0
  n = strcspn (line, " \t\n");
203
0
  if (!n)
204
0
    {
205
      /* Print all commands.  If a help string is available and that
206
         starts with the command name, print the first line of the
207
         help string.  */
208
0
      for (i = 0; i < ctx->cmdtbl_used; i++)
209
0
        {
210
0
          n = strlen (ctx->cmdtbl[i].name);
211
0
          helpstr = ctx->cmdtbl[i].helpstr;
212
0
          if (helpstr
213
0
              && !strncmp (ctx->cmdtbl[i].name, helpstr, n)
214
0
              && (!helpstr[n] || helpstr[n] == '\n' || helpstr[n] == ' ')
215
0
              && (n = strcspn (helpstr, "\n"))          )
216
0
            snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr);
217
0
          else
218
0
            snprintf (buf, sizeof (buf), "# %s", ctx->cmdtbl[i].name);
219
0
          buf[ASSUAN_LINELENGTH - 1] = '\0';
220
0
          assuan_write_line (ctx, buf);
221
0
        }
222
0
    }
223
0
  else
224
0
    {
225
      /* Print the help for the given command.  */
226
0
      int c = line[n];
227
0
      line[n] = 0;
228
0
      for (i=0; ctx->cmdtbl[i].name; i++)
229
0
        if (!my_strcasecmp (line, ctx->cmdtbl[i].name))
230
0
          break;
231
0
      line[n] = c;
232
0
      if (!ctx->cmdtbl[i].name)
233
0
        return PROCESS_DONE (ctx, set_error (ctx,GPG_ERR_UNKNOWN_COMMAND,NULL));
234
0
      helpstr = ctx->cmdtbl[i].helpstr;
235
0
      if (!helpstr)
236
0
        return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_FOUND, NULL));
237
0
      do
238
0
        {
239
0
          n = strcspn (helpstr, "\n");
240
0
          snprintf (buf, sizeof (buf), "# %.*s", (int)n, helpstr);
241
0
          helpstr += n;
242
0
          if (*helpstr == '\n')
243
0
            helpstr++;
244
0
          buf[ASSUAN_LINELENGTH - 1] = '\0';
245
0
          assuan_write_line (ctx, buf);
246
0
        }
247
0
      while (*helpstr);
248
0
    }
249
250
0
  return PROCESS_DONE (ctx, 0);
251
0
}
252
253
static const char std_help_end[] =
254
  "END\n"
255
  "\n"
256
  "Used by a client to mark the end of raw data.";
257
static gpg_error_t
258
std_handler_end (assuan_context_t ctx, char *line)
259
0
{
260
0
  return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL));
261
0
}
262
263
264
gpg_error_t
265
assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd)
266
0
{
267
0
  char *endp;
268
269
0
  if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2))
270
0
      || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2])))
271
0
    return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=<n>] expected");
272
0
  line += 2;
273
0
  if (*line == '=')
274
0
    {
275
0
      line ++;
276
0
      if (!digitp (*line))
277
0
  return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required");
278
#if HAVE_W64_SYSTEM
279
      *rfd = (void*)strtoull (line, &endp, 10);
280
#elif HAVE_W32_SYSTEM
281
      *rfd = (void*)strtoul (line, &endp, 10);
282
#else
283
0
      *rfd = strtoul (line, &endp, 10);
284
0
#endif
285
      /* Remove that argument so that a notify handler won't see it. */
286
0
      memset (line, ' ', endp? (endp-line):strlen(line));
287
288
0
      if (*rfd == ctx->inbound.fd)
289
0
  return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd");
290
0
      if (*rfd == ctx->outbound.fd)
291
0
  return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd");
292
0
      return 0;
293
0
    }
294
0
  else
295
    /* Our peer has sent the file descriptor.  */
296
0
    return assuan_receivefd (ctx, rfd);
297
0
}
298
299
300
static const char std_help_input[] =
301
  "INPUT FD[=<N>]\n"
302
  "\n"
303
  "Used by a client to pass an input file descriptor to the server.\n"
304
  "The server opens <N> as a local file descriptor.  Without <N>, the\n"
305
  "server opens the file descriptor just sent by the client using\n"
306
  "assuan_sendfd.";
307
static gpg_error_t
308
std_handler_input (assuan_context_t ctx, char *line)
309
0
{
310
0
  gpg_error_t rc;
311
0
  assuan_fd_t fd, oldfd;
312
313
0
  rc = assuan_command_parse_fd (ctx, line, &fd);
314
0
  if (rc)
315
0
    return PROCESS_DONE (ctx, rc);
316
317
0
  if (ctx->input_notify_fnc)
318
0
    {
319
0
      oldfd = ctx->input_fd;
320
0
      ctx->input_fd = fd;
321
0
      rc = ctx->input_notify_fnc (ctx, line);
322
0
      if (rc)
323
0
        ctx->input_fd = oldfd;
324
0
    }
325
0
  else if (!rc)
326
0
    ctx->input_fd = fd;
327
0
  return PROCESS_DONE (ctx, rc);
328
0
}
329
330
331
static const char std_help_output[] =
332
  "OUTPUT FD[=<N>]\n"
333
  "\n"
334
  "Used by a client to pass an output file descriptor to the server.\n"
335
  "The server opens <N> as a local file descriptor.  Without <N>, the\n"
336
  "server opens the file descriptor just sent by the client using\n"
337
  "assuan_sendfd.";
338
static gpg_error_t
339
std_handler_output (assuan_context_t ctx, char *line)
340
0
{
341
0
  gpg_error_t rc;
342
0
  assuan_fd_t fd, oldfd;
343
344
0
  rc = assuan_command_parse_fd (ctx, line, &fd);
345
0
  if (rc)
346
0
    return PROCESS_DONE (ctx, rc);
347
348
0
  if (ctx->output_notify_fnc)
349
0
    {
350
0
      oldfd = ctx->output_fd;
351
0
      ctx->output_fd = fd;
352
0
      rc = ctx->output_notify_fnc (ctx, line);
353
0
      if (rc)
354
0
        ctx->output_fd = oldfd;
355
0
    }
356
0
  else if (!rc)
357
0
    ctx->output_fd = fd;
358
0
  return PROCESS_DONE (ctx, rc);
359
0
}
360
361
362
#if HAVE_W32_SYSTEM || HAVE_W64_SYSTEM
363
/*
364
 * The command used by a client to send FD.  That is, from the viewpoint
365
 * of handling this command, it is to _receive_ a file handle.
366
 */
367
static const char w32_help_sendfd[] =
368
  "SENDFD <N>\n"
369
  "\n"
370
  "Used by a client to pass a file HANDLE to the server.\n"
371
  "The server opens <N> as a local file HANDLE.";
372
static gpg_error_t
373
w32_handler_sendfd (assuan_context_t ctx, char *line)
374
{
375
  gpg_error_t err = 0;
376
  char *endp;
377
  intptr_t file_handle;
378
  int fd;
379
380
#if HAVE_W64_SYSTEM
381
  file_handle = strtoull (line, &endp, 16);
382
#elif HAVE_W32_SYSTEM
383
  file_handle = strtoul (line, &endp, 16);
384
#endif
385
386
  if (*endp)
387
    {
388
      err = set_error (ctx, GPG_ERR_ASS_SYNTAX, "hex number required");
389
      return PROCESS_DONE (ctx, err);
390
    }
391
392
  fd = _open_osfhandle ((intptr_t)file_handle, _O_RDWR);
393
  if (fd < 0)
394
    {
395
      CloseHandle ((HANDLE)file_handle);
396
      err = GPG_ERR_ASSUAN;
397
    }
398
399
  ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = (assuan_fd_t)fd;
400
  return PROCESS_DONE (ctx, err);
401
}
402
#endif
403
404
/* This is a table with the standard commands and handler for them.
405
   The table is used to initialize a new context and associate strings
406
   with default handlers */
407
static struct {
408
  const char *name;
409
  gpg_error_t (*handler)(assuan_context_t, char *line);
410
  const char *help;
411
  int always; /* always initialize this command */
412
} std_cmd_table[] = {
413
  { "NOP",    std_handler_nop, std_help_nop, 1 },
414
  { "CANCEL", std_handler_cancel, std_help_cancel, 1 },
415
  { "OPTION", std_handler_option, std_help_option, 1 },
416
  { "BYE",    std_handler_bye, std_help_bye, 1 },
417
  { "AUTH",   std_handler_auth, std_help_auth, 1 },
418
  { "RESET",  std_handler_reset, std_help_reset, 1 },
419
  { "END",    std_handler_end, std_help_end, 1 },
420
  { "HELP",   std_handler_help, std_help_help, 1 },
421
422
  { "INPUT",  std_handler_input, std_help_input, 0 },
423
  { "OUTPUT", std_handler_output, std_help_output, 0 },
424
#if HAVE_W32_SYSTEM
425
  { "SENDFD",  w32_handler_sendfd, w32_help_sendfd, 1 },
426
#endif
427
  { } };
428
429
430
/**
431
 * assuan_register_command:
432
 * @ctx: the server context
433
 * @cmd_name: A string with the command name
434
 * @handler: The handler function to be called or NULL to use a default
435
 *           handler.
436
 * HELPSTRING
437
 *
438
 * Register a handler to be used for a given command.  Note that
439
 * several default handlers are already registered with a new context.
440
 * This function however allows to override them.
441
 *
442
 * Return value: 0 on success or an error code
443
 **/
444
gpg_error_t
445
assuan_register_command (assuan_context_t ctx, const char *cmd_name,
446
                         assuan_handler_t handler, const char *help_string)
447
0
{
448
0
  int i, cmd_index = -1;
449
0
  const char *s;
450
451
0
  if (cmd_name && !*cmd_name)
452
0
    cmd_name = NULL;
453
454
0
  if (!cmd_name)
455
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
456
457
0
  if (!handler)
458
0
    { /* find a default handler. */
459
0
      for (i=0; (s=std_cmd_table[i].name) && strcmp (cmd_name, s); i++)
460
0
        ;
461
0
      if (!s)
462
0
        { /* Try again but case insensitive. */
463
0
          for (i=0; (s=std_cmd_table[i].name)
464
0
                    && my_strcasecmp (cmd_name, s); i++)
465
0
            ;
466
0
        }
467
0
      if (s)
468
0
        handler = std_cmd_table[i].handler;
469
0
      if (!handler)
470
0
        handler = dummy_handler; /* Last resort is the dummy handler. */
471
0
    }
472
473
0
  if (!ctx->cmdtbl)
474
0
    {
475
0
      ctx->cmdtbl_size = 50;
476
0
      ctx->cmdtbl = _assuan_calloc (ctx, ctx->cmdtbl_size, sizeof *ctx->cmdtbl);
477
0
      if (!ctx->cmdtbl)
478
0
  return _assuan_error (ctx, gpg_err_code_from_syserror ());
479
0
      ctx->cmdtbl_used = 0;
480
0
    }
481
0
  else if (ctx->cmdtbl_used >= ctx->cmdtbl_size)
482
0
    {
483
0
      struct cmdtbl_s *x;
484
485
0
      x = _assuan_realloc (ctx, ctx->cmdtbl, (ctx->cmdtbl_size+10) * sizeof *x);
486
0
      if (!x)
487
0
  return _assuan_error (ctx, gpg_err_code_from_syserror ());
488
0
      ctx->cmdtbl = x;
489
0
      ctx->cmdtbl_size += 50;
490
0
    }
491
492
0
  for (i=0; i<ctx->cmdtbl_used; i++)
493
0
    {
494
0
      if (!my_strcasecmp (cmd_name, ctx->cmdtbl[i].name))
495
0
        {
496
0
    cmd_index = i;
497
0
    break;
498
0
  }
499
0
    }
500
501
0
  if (cmd_index == -1)
502
0
    cmd_index = ctx->cmdtbl_used++;
503
504
0
  ctx->cmdtbl[cmd_index].name = cmd_name;
505
0
  ctx->cmdtbl[cmd_index].handler = handler;
506
0
  ctx->cmdtbl[cmd_index].helpstr = help_string;
507
0
  return 0;
508
0
}
509
510
/* Return the name of the command currently processed by a handler.
511
   The string returned is valid until the next call to an assuan
512
   function on the same context.  Returns NULL if no handler is
513
   executed or the command is not known.  */
514
const char *
515
assuan_get_command_name (assuan_context_t ctx)
516
0
{
517
0
  return ctx? ctx->current_cmd_name : NULL;
518
0
}
519
520
gpg_error_t
521
assuan_register_pre_cmd_notify (assuan_context_t ctx,
522
                                 gpg_error_t (*fnc)(assuan_context_t,
523
           const char *cmd))
524
0
{
525
0
  if (!ctx)
526
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
527
0
  ctx->pre_cmd_notify_fnc = fnc;
528
0
  return 0;
529
0
}
530
531
gpg_error_t
532
assuan_register_post_cmd_notify (assuan_context_t ctx,
533
                                 void (*fnc)(assuan_context_t, gpg_error_t))
534
0
{
535
0
  if (!ctx)
536
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
537
0
  ctx->post_cmd_notify_fnc = fnc;
538
0
  return 0;
539
0
}
540
541
gpg_error_t
542
assuan_register_bye_notify (assuan_context_t ctx, assuan_handler_t fnc)
543
0
{
544
0
  if (!ctx)
545
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
546
0
  ctx->bye_notify_fnc = fnc;
547
0
  return 0;
548
0
}
549
550
gpg_error_t
551
assuan_register_reset_notify (assuan_context_t ctx, assuan_handler_t fnc)
552
0
{
553
0
  if (!ctx)
554
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
555
0
  ctx->reset_notify_fnc = fnc;
556
0
  return 0;
557
0
}
558
559
gpg_error_t
560
assuan_register_cancel_notify (assuan_context_t ctx, assuan_handler_t fnc)
561
0
{
562
0
  if (!ctx)
563
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
564
0
  ctx->cancel_notify_fnc = fnc;
565
0
  return 0;
566
0
}
567
568
gpg_error_t
569
assuan_register_option_handler (assuan_context_t ctx,
570
        gpg_error_t (*fnc)(assuan_context_t,
571
               const char*, const char*))
572
0
{
573
0
  if (!ctx)
574
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
575
0
  ctx->option_handler_fnc = fnc;
576
0
  return 0;
577
0
}
578
579
gpg_error_t
580
assuan_register_input_notify (assuan_context_t ctx, assuan_handler_t fnc)
581
0
{
582
0
  if (!ctx)
583
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
584
0
  ctx->input_notify_fnc = fnc;
585
0
  return 0;
586
0
}
587
588
gpg_error_t
589
assuan_register_output_notify (assuan_context_t ctx, assuan_handler_t fnc)
590
0
{
591
0
  if (!ctx)
592
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
593
0
  ctx->output_notify_fnc = fnc;
594
0
  return 0;
595
0
}
596
597
598
/* Helper to register the standards commands */
599
gpg_error_t
600
_assuan_register_std_commands (assuan_context_t ctx)
601
0
{
602
0
  gpg_error_t rc;
603
0
  int i;
604
605
0
  for (i = 0; std_cmd_table[i].name; i++)
606
0
    {
607
0
      if (std_cmd_table[i].always)
608
0
        {
609
0
          rc = assuan_register_command (ctx, std_cmd_table[i].name, NULL, NULL);
610
0
          if (rc)
611
0
            return rc;
612
0
        }
613
0
    }
614
0
  return 0;
615
0
}
616
617
618

619
/* Process the special data lines.  The "D " has already been removed
620
   from the line.  As all handlers this function may modify the line.  */
621
static gpg_error_t
622
handle_data_line (assuan_context_t ctx, char *line, int linelen)
623
0
{
624
0
  return set_error (ctx, GPG_ERR_NOT_IMPLEMENTED, NULL);
625
0
}
626
627
/* like ascii_strcasecmp but assume that B is already uppercase */
628
static int
629
my_strcasecmp (const char *a, const char *b)
630
0
{
631
0
    if (a == b)
632
0
        return 0;
633
634
0
    for (; *a && *b; a++, b++)
635
0
      {
636
0
  if (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) != *b)
637
0
      break;
638
0
      }
639
0
    return *a == *b? 0 : (((*a >= 'a' && *a <= 'z')? (*a&~0x20):*a) - *b);
640
0
}
641
642
643
/* Parse the line, break out the command, find it in the command
644
   table, remove leading and white spaces from the arguments, call the
645
   handler with the argument line and return the error.  */
646
static gpg_error_t
647
dispatch_command (assuan_context_t ctx, char *line, int linelen)
648
0
{
649
0
  gpg_error_t err;
650
0
  char *p;
651
0
  const char *s;
652
0
  int shift, i;
653
654
  /* Note that as this function is invoked by assuan_process_next as
655
     well, we need to hide non-critical errors with PROCESS_DONE.  */
656
657
0
  if (*line == 'D' && line[1] == ' ') /* divert to special handler */
658
    /* FIXME: Depending on the final implementation of
659
       handle_data_line, this may be wrong here.  For example, if a
660
       user callback is invoked, and that callback is responsible for
661
       calling assuan_process_done, then this is wrong.  */
662
0
    return PROCESS_DONE (ctx, handle_data_line (ctx, line+2, linelen-2));
663
664
0
  for (p=line; *p && *p != ' ' && *p != '\t'; p++)
665
0
    ;
666
0
  if (p==line)
667
0
    return PROCESS_DONE
668
0
      (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "leading white-space"));
669
0
  if (*p)
670
0
    { /* Skip over leading WS after the keyword */
671
0
      *p++ = 0;
672
0
      while ( *p == ' ' || *p == '\t')
673
0
        p++;
674
0
    }
675
0
  shift = p - line;
676
677
0
  for (i=0; (s=ctx->cmdtbl[i].name); i++)
678
0
    {
679
0
      if (!strcmp (line, s))
680
0
        break;
681
0
    }
682
0
  if (!s)
683
0
    { /* and try case insensitive */
684
0
      for (i=0; (s=ctx->cmdtbl[i].name); i++)
685
0
        {
686
0
          if (!my_strcasecmp (line, s))
687
0
            break;
688
0
        }
689
0
    }
690
0
  if (!s)
691
0
    return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_UNKNOWN_CMD, NULL));
692
0
  line += shift;
693
  /* linelen -= shift; -- not needed.  */
694
695
0
  if (ctx->pre_cmd_notify_fnc) {
696
0
    err = ctx->pre_cmd_notify_fnc(ctx, ctx->cmdtbl[i].name);
697
698
0
    if (err)
699
0
      return PROCESS_DONE(ctx, err);
700
0
  }
701
702
/*    fprintf (stderr, "DBG-assuan: processing %s `%s'\n", s, line); */
703
0
  ctx->current_cmd_name = ctx->cmdtbl[i].name;
704
0
  err = ctx->cmdtbl[i].handler (ctx, line);
705
0
  ctx->current_cmd_name = NULL;
706
0
  return err;
707
0
}
708
709

710
/* Call this to acknowledge the current command.  */
711
gpg_error_t
712
assuan_process_done (assuan_context_t ctx, gpg_error_t rc)
713
0
{
714
0
  if (!ctx->flags.in_command)
715
0
    return _assuan_error (ctx, GPG_ERR_ASS_GENERAL);
716
717
0
  if (ctx->flags.force_close)
718
0
    ctx->flags.process_complete = 1;
719
720
0
  ctx->flags.in_command = 0;
721
722
  /* Check for data write errors.  */
723
0
  if (ctx->outbound.data.fp)
724
0
    {
725
      /* Flush the data lines.  */
726
0
      fclose (ctx->outbound.data.fp);
727
0
      ctx->outbound.data.fp = NULL;
728
0
      if (!rc && ctx->outbound.data.error)
729
0
  rc = ctx->outbound.data.error;
730
0
    }
731
0
  else
732
0
    {
733
      /* Flush any data send without using the data FP.  */
734
0
      assuan_send_data (ctx, NULL, 0);
735
0
      if (!rc && ctx->outbound.data.error)
736
0
  rc = ctx->outbound.data.error;
737
0
    }
738
739
  /* Error handling.  */
740
0
  if (!rc)
741
0
    {
742
0
      if (ctx->flags.process_complete)
743
0
  {
744
    /* No error checking because the peer may have already
745
       disconnect. */
746
0
    assuan_write_line (ctx, "OK closing connection");
747
0
    ctx->finish_handler (ctx);
748
0
  }
749
0
      else
750
0
  rc = assuan_write_line (ctx, ctx->okay_line ? ctx->okay_line : "OK");
751
0
    }
752
0
  else
753
0
    {
754
0
      char errline[300];
755
0
      const char *text = ctx->err_no == rc ? ctx->err_str : NULL;
756
0
      char ebuf[50];
757
758
0
      if (ctx->flags.force_close)
759
0
        text = "[closing connection]";
760
761
0
      gpg_strerror_r (rc, ebuf, sizeof (ebuf));
762
0
      snprintf (errline, sizeof errline, "ERR %d %.50s <%.30s>%s%.100s",
763
0
                rc, ebuf, gpg_strsource (rc),
764
0
                text? " - ":"", text?text:"");
765
766
0
      rc = assuan_write_line (ctx, errline);
767
768
0
      if (ctx->flags.force_close)
769
0
        ctx->finish_handler (ctx);
770
0
    }
771
772
0
  if (ctx->post_cmd_notify_fnc)
773
0
    ctx->post_cmd_notify_fnc (ctx, rc);
774
775
0
  ctx->flags.confidential = 0;
776
0
  if (ctx->okay_line)
777
0
    {
778
0
      _assuan_free (ctx, ctx->okay_line);
779
0
      ctx->okay_line = NULL;
780
0
    }
781
782
0
  return rc;
783
0
}
784
785
786
static gpg_error_t
787
process_next (assuan_context_t ctx)
788
0
{
789
0
  gpg_error_t rc;
790
791
  /* What the next thing to do is depends on the current state.
792
     However, we will always first read the next line.  The client is
793
     required to write full lines without blocking long after starting
794
     a partial line.  */
795
0
  rc = _assuan_read_line (ctx);
796
0
  if (_assuan_error_is_eagain (ctx, rc))
797
0
    return 0;
798
0
  if (gpg_err_code (rc) == GPG_ERR_EOF)
799
0
    {
800
0
      ctx->flags.process_complete = 1;
801
0
      return 0;
802
0
    }
803
0
  if (rc)
804
0
    return rc;
805
0
  if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
806
     /* Comment lines are ignored.  */
807
0
    return 0;
808
809
  /* Now we have a line that really means something.  It could be one
810
     of the following things: First, if we are not in a command
811
     already, it is the next command to dispatch.  Second, if we are
812
     in a command, it can only be the response to an INQUIRE
813
     reply.  */
814
815
0
  if (!ctx->flags.in_command)
816
0
    {
817
0
      ctx->flags.in_command = 1;
818
819
0
      ctx->outbound.data.error = 0;
820
0
      ctx->outbound.data.linelen = 0;
821
      /* Dispatch command and return reply.  */
822
0
      ctx->flags.in_process_next = 1;
823
0
      rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
824
0
      ctx->flags.in_process_next = 0;
825
0
    }
826
0
  else if (ctx->flags.in_inquire)
827
0
    {
828
      /* FIXME: Pick up the continuation.  */
829
0
      rc = _assuan_inquire_ext_cb (ctx);
830
0
    }
831
0
  else
832
0
    {
833
      /* Should not happen.  The client is sending data while we are
834
   in a command and not waiting for an inquire.  We log an error
835
   and discard it.  */
836
0
      TRACE0 (ctx, ASSUAN_LOG_DATA, "process_next", ctx,
837
0
        "unexpected client data");
838
0
      rc = 0;
839
0
    }
840
841
0
  return rc;
842
0
}
843
844
845
/* This function should be invoked when the assuan connected FD is
846
   ready for reading.  If the equivalent to EWOULDBLOCK is returned
847
   (this should be done by the command handler), assuan_process_next
848
   should be invoked the next time the connected FD is readable.
849
   Eventually, the caller will finish by invoking assuan_process_done.
850
   DONE is set to 1 if the connection has ended.  */
851
gpg_error_t
852
assuan_process_next (assuan_context_t ctx, int *done)
853
0
{
854
0
  gpg_error_t rc;
855
856
0
  if (done)
857
0
    *done = 0;
858
0
  ctx->flags.process_complete = 0;
859
0
  do
860
0
    {
861
0
      rc = process_next (ctx);
862
0
    }
863
0
  while (!rc && !ctx->flags.process_complete && assuan_pending_line (ctx));
864
865
0
  if (done)
866
0
    *done = !!ctx->flags.process_complete;
867
868
0
  return rc;
869
0
}
870
871
872

873
static gpg_error_t
874
process_request (assuan_context_t ctx)
875
0
{
876
0
  gpg_error_t rc;
877
878
0
  if (ctx->flags.in_inquire)
879
0
    return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS);
880
881
0
  do
882
0
    {
883
0
      rc = _assuan_read_line (ctx);
884
0
    }
885
0
  while (_assuan_error_is_eagain (ctx, rc));
886
0
  if (gpg_err_code (rc) == GPG_ERR_EOF)
887
0
    {
888
0
      ctx->flags.process_complete = 1;
889
0
      return 0;
890
0
    }
891
0
  if (rc)
892
0
    return rc;
893
0
  if (*ctx->inbound.line == '#' || !ctx->inbound.linelen)
894
0
    return 0; /* comment line - ignore */
895
896
0
  ctx->flags.in_command = 1;
897
0
  ctx->outbound.data.error = 0;
898
0
  ctx->outbound.data.linelen = 0;
899
  /* dispatch command and return reply */
900
0
  rc = dispatch_command (ctx, ctx->inbound.line, ctx->inbound.linelen);
901
902
0
  return assuan_process_done (ctx, rc);
903
0
}
904
905
/**
906
 * assuan_process:
907
 * @ctx: assuan context
908
 *
909
 * This function is used to handle the assuan protocol after a
910
 * connection has been established using assuan_accept().  This is the
911
 * main protocol handler.
912
 *
913
 * Return value: 0 on success or an error code if the assuan operation
914
 * failed.  Note, that no error is returned for operational errors.
915
 **/
916
gpg_error_t
917
assuan_process (assuan_context_t ctx)
918
0
{
919
0
  gpg_error_t rc;
920
921
0
  ctx->flags.process_complete = 0;
922
0
  do {
923
0
    rc = process_request (ctx);
924
0
  } while (!rc && !ctx->flags.process_complete);
925
926
0
  return rc;
927
0
}
928
929
930
/**
931
 * assuan_get_active_fds:
932
 * @ctx: Assuan context
933
 * @what: 0 for read fds, 1 for write fds
934
 * @fdarray: Caller supplied array to store the FDs
935
 * @fdarraysize: size of that array
936
 *
937
 * Return all active filedescriptors for the given context.  This
938
 * function can be used to select on the fds and call
939
 * assuan_process_next() if there is an active one.  The first fd in
940
 * the array is the one used for the command connection.
941
 *
942
 * Note, that write FDs are not yet supported.
943
 *
944
 * Return value: number of FDs active and put into @fdarray or -1 on
945
 * error which is most likely a too small fdarray.
946
 **/
947
int
948
assuan_get_active_fds (assuan_context_t ctx, int what,
949
                       assuan_fd_t *fdarray, int fdarraysize)
950
0
{
951
0
  int n = 0;
952
953
0
  if (!ctx || fdarraysize < 2 || what < 0 || what > 1)
954
0
    return -1;
955
956
0
  if (!what)
957
0
    {
958
0
      if (ctx->inbound.fd != ASSUAN_INVALID_FD)
959
0
        fdarray[n++] = ctx->inbound.fd;
960
0
    }
961
0
  else
962
0
    {
963
0
      if (ctx->outbound.fd != ASSUAN_INVALID_FD)
964
0
        fdarray[n++] = ctx->outbound.fd;
965
0
      if (ctx->outbound.data.fp)
966
#if defined(HAVE_W32_SYSTEM)
967
        fdarray[n++] = (void*)_get_osfhandle (fileno (ctx->outbound.data.fp));
968
#else
969
0
        fdarray[n++] = fileno (ctx->outbound.data.fp);
970
0
#endif
971
0
    }
972
973
0
  return n;
974
0
}
975
976
977
/* Two simple wrappers to make the expected function types match. */
978
#ifdef HAVE_FUNOPEN
979
static int
980
fun1_cookie_write (void *cookie, const char *buffer, int orig_size)
981
{
982
  return _assuan_cookie_write_data (cookie, buffer, orig_size);
983
}
984
#endif /*HAVE_FUNOPEN*/
985
#ifdef HAVE_FOPENCOOKIE
986
static ssize_t
987
fun2_cookie_write (void *cookie, const char *buffer, size_t orig_size)
988
0
{
989
0
  return _assuan_cookie_write_data (cookie, buffer, orig_size);
990
0
}
991
#endif /*HAVE_FOPENCOOKIE*/
992
993
/* Return a FP to be used for data output.  The FILE pointer is valid
994
   until the end of a handler.  So a close is not needed.  Assuan does
995
   all the buffering needed to insert the status line as well as the
996
   required line wappping and quoting for data lines.
997
998
   We use GNU's custom streams here.  There should be an alternative
999
   implementaion for systems w/o a glibc, a simple implementation
1000
   could use a child process */
1001
FILE *
1002
assuan_get_data_fp (assuan_context_t ctx)
1003
0
{
1004
0
#if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
1005
0
  if (ctx->outbound.data.fp)
1006
0
    return ctx->outbound.data.fp;
1007
1008
#ifdef HAVE_FUNOPEN
1009
  ctx->outbound.data.fp = funopen (ctx, 0, fun1_cookie_write,
1010
           0, _assuan_cookie_write_flush);
1011
#else
1012
0
  ctx->outbound.data.fp = funopen (ctx, 0, fun2_cookie_write,
1013
0
           0, _assuan_cookie_write_flush);
1014
0
#endif
1015
1016
0
  ctx->outbound.data.error = 0;
1017
0
  return ctx->outbound.data.fp;
1018
#else
1019
  gpg_err_set_errno (ENOSYS);
1020
  return NULL;
1021
#endif
1022
0
}
1023
1024
1025
/* Set the text used for the next OK response.  This string is
1026
   automatically reset to NULL after the next command. */
1027
gpg_error_t
1028
assuan_set_okay_line (assuan_context_t ctx, const char *line)
1029
0
{
1030
0
  if (!ctx)
1031
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
1032
0
  if (!line)
1033
0
    {
1034
0
      _assuan_free (ctx, ctx->okay_line);
1035
0
      ctx->okay_line = NULL;
1036
0
    }
1037
0
  else
1038
0
    {
1039
      /* FIXME: we need to use gcry_is_secure() to test whether
1040
         we should allocate the entire line in secure memory */
1041
0
      char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1);
1042
0
      if (!buf)
1043
0
        return _assuan_error (ctx, gpg_err_code_from_syserror ());
1044
0
      strcpy (buf, "OK ");
1045
0
      strcpy (buf+3, line);
1046
0
      _assuan_free (ctx, ctx->okay_line);
1047
0
      ctx->okay_line = buf;
1048
0
    }
1049
0
  return 0;
1050
0
}
1051
1052
1053
1054
gpg_error_t
1055
assuan_write_status (assuan_context_t ctx,
1056
                     const char *keyword, const char *text)
1057
0
{
1058
0
  char buffer[256];
1059
0
  char *helpbuf;
1060
0
  size_t n;
1061
0
  gpg_error_t ae;
1062
1063
0
  if ( !ctx || !keyword)
1064
0
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
1065
0
  if (!text)
1066
0
    text = "";
1067
1068
0
  n = 2 + strlen (keyword) + 1 + strlen (text) + 1;
1069
0
  if (n < sizeof (buffer))
1070
0
    {
1071
0
      strcpy (buffer, "S ");
1072
0
      strcat (buffer, keyword);
1073
0
      if (*text)
1074
0
        {
1075
0
          strcat (buffer, " ");
1076
0
          strcat (buffer, text);
1077
0
        }
1078
0
      ae = assuan_write_line (ctx, buffer);
1079
0
    }
1080
0
  else if ( (helpbuf = _assuan_malloc (ctx, n)) )
1081
0
    {
1082
0
      strcpy (helpbuf, "S ");
1083
0
      strcat (helpbuf, keyword);
1084
0
      if (*text)
1085
0
        {
1086
0
          strcat (helpbuf, " ");
1087
0
          strcat (helpbuf, text);
1088
0
        }
1089
0
      ae = assuan_write_line (ctx, helpbuf);
1090
0
      _assuan_free (ctx, helpbuf);
1091
0
    }
1092
0
  else
1093
0
    ae = 0;
1094
0
  return ae;
1095
0
}