~ubuntu-branches/ubuntu/precise/gnupg2/precise-proposed

« back to all changes in this revision

Viewing changes to common/simple-pwquery.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Mueller
  • Date: 2005-03-29 10:30:32 UTC
  • Revision ID: james.westby@ubuntu.com-20050329103032-sj42n2ain3ipx310
Tags: upstream-1.9.15
ImportĀ upstreamĀ versionĀ 1.9.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* simple-pwquery.c - A simple password query cleint for gpg-agent
 
2
 *      Copyright (C) 2002, 2004 Free Software Foundation, Inc.
 
3
 *
 
4
 * This file is part of GnuPG.
 
5
 *
 
6
 * GnuPG is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * GnuPG is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
19
 */
 
20
 
 
21
/* This module is intended as a standalone client implementation to
 
22
   gpg-agent's GET_PASSPHRASE command.  In particular it does not use
 
23
   the Assuan library and can only cope with an already running
 
24
   gpg-agent.  Some stuff is configurable in the header file. */
 
25
 
 
26
#ifdef HAVE_CONFIG_H
 
27
#include <config.h>
 
28
#endif
 
29
#include <stdlib.h>
 
30
#include <stddef.h>
 
31
#include <string.h>
 
32
#include <errno.h>
 
33
#include <unistd.h>
 
34
#ifdef HAVE_W32_SYSTEM
 
35
#include <winsock2.h>
 
36
#else
 
37
#include <sys/socket.h>
 
38
#include <sys/un.h>
 
39
#endif
 
40
#ifdef HAVE_LOCALE_H
 
41
#include <locale.h>
 
42
#endif
 
43
#ifdef HAVE_W32_SYSTEM
 
44
#include "../jnlib/w32-afunix.h"
 
45
#endif
 
46
 
 
47
 
 
48
#define SIMPLE_PWQUERY_IMPLEMENTATION 1
 
49
#include "simple-pwquery.h"
 
50
 
 
51
#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
 
52
# undef SPWQ_USE_LOGGING
 
53
#endif
 
54
 
 
55
#ifndef _
 
56
#define _(a) (a)
 
57
#endif
 
58
 
 
59
#if !defined (hexdigitp) && !defined (xtoi_2)
 
60
#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
 
61
#define hexdigitp(a) (digitp (a)                     \
 
62
                      || (*(a) >= 'A' && *(a) <= 'F')  \
 
63
                      || (*(a) >= 'a' && *(a) <= 'f'))
 
64
#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
 
65
                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
 
66
#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
 
67
#endif
 
68
 
 
69
 
 
70
 
 
71
 
 
72
 
 
73
 
 
74
#ifndef HAVE_STPCPY
 
75
static char *
 
76
my_stpcpy(char *a,const char *b)
 
77
{
 
78
    while( *b )
 
79
        *a++ = *b++;
 
80
    *a = 0;
 
81
 
 
82
    return (char*)a;
 
83
}
 
84
#define stpcpy(a,b)  my_stpcpy((a), (b))
 
85
#endif
 
86
 
 
87
 
 
88
 
 
89
/* Write NBYTES of BUF to file descriptor FD. */
 
90
static int
 
91
writen (int fd, const void *buf, size_t nbytes)
 
92
{
 
93
  size_t nleft = nbytes;
 
94
  int nwritten;
 
95
  
 
96
  while (nleft > 0)
 
97
    {
 
98
#ifdef HAVE_W32_SYSTEM
 
99
      nwritten = send (fd, buf, nleft, 0);
 
100
#else
 
101
      nwritten = write (fd, buf, nleft);
 
102
#endif
 
103
      if (nwritten < 0)
 
104
        {
 
105
          if (errno == EINTR)
 
106
            nwritten = 0;
 
107
          else {
 
108
#ifdef SPWQ_USE_LOGGING
 
109
            log_error ("write failed: %s\n", strerror (errno));
 
110
#endif
 
111
            return SPWQ_IO_ERROR;
 
112
          }
 
113
        }
 
114
      nleft -= nwritten;
 
115
      buf = (const char*)buf + nwritten;
 
116
    }
 
117
    
 
118
  return 0;
 
119
}
 
120
 
 
121
 
 
122
/* Read an entire line and return number of bytes read. */
 
123
static int
 
124
readline (int fd, char *buf, size_t buflen)
 
125
{
 
126
  size_t nleft = buflen;
 
127
  char *p;
 
128
  int nread = 0;
 
129
 
 
130
  while (nleft > 0)
 
131
    {
 
132
#ifdef HAVE_W32_SYSTEM
 
133
      int n = recv (fd, buf, nleft, 0);
 
134
#else
 
135
      int n = read (fd, buf, nleft);
 
136
#endif
 
137
      if (n < 0)
 
138
        {
 
139
          if (errno == EINTR)
 
140
            continue;
 
141
          return -(SPWQ_IO_ERROR);
 
142
        }
 
143
      else if (!n)
 
144
        {
 
145
          return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
 
146
        }
 
147
      p = buf;
 
148
      nleft -= n;
 
149
      buf += n;
 
150
      nread += n;
 
151
      
 
152
      for (; n && *p != '\n'; n--, p++)
 
153
        ;
 
154
      if (n)
 
155
        {
 
156
          break; /* at least one full line available - that's enough.
 
157
                    This function is just a simple implementation, so
 
158
                    it is okay to forget about pending bytes */
 
159
        }
 
160
    }
 
161
 
 
162
  return nread; 
 
163
}
 
164
 
 
165
 
 
166
/* Send an option to the agent */
 
167
static int
 
168
agent_send_option (int fd, const char *name, const char *value)
 
169
{
 
170
  char buf[200];
 
171
  int nread;
 
172
  char *line;
 
173
  int i; 
 
174
  
 
175
  line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
 
176
  if (!line)
 
177
    return SPWQ_OUT_OF_CORE;
 
178
  strcpy (stpcpy (stpcpy (stpcpy (
 
179
                     stpcpy (line, "OPTION "), name), "="), value), "\n");
 
180
  i = writen (fd, line, strlen (line));
 
181
  spwq_free (line);
 
182
  if (i)
 
183
    return i;
 
184
  
 
185
  /* get response */
 
186
  nread = readline (fd, buf, DIM(buf)-1);
 
187
  if (nread < 0)
 
188
    return -nread;
 
189
  if (nread < 3)
 
190
    return SPWQ_PROTOCOL_ERROR;
 
191
  
 
192
  if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) 
 
193
    return 0; /* okay */
 
194
 
 
195
  return SPWQ_ERR_RESPONSE;
 
196
}
 
197
 
 
198
 
 
199
/* Send all available options to the agent. */
 
200
static int 
 
201
agent_send_all_options (int fd)
 
202
{
 
203
  char *dft_display = NULL;
 
204
  char *dft_ttyname = NULL;
 
205
  char *dft_ttytype = NULL;
 
206
  int rc = 0;
 
207
 
 
208
  dft_display = getenv ("DISPLAY");
 
209
  if (dft_display)
 
210
    {
 
211
      if ((rc = agent_send_option (fd, "display", dft_display)))
 
212
        return rc;
 
213
    }
 
214
 
 
215
  dft_ttyname = getenv ("GPG_TTY");
 
216
#ifndef HAVE_W32_SYSTEM
 
217
  if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
 
218
    dft_ttyname = ttyname (0);
 
219
#endif
 
220
  if (dft_ttyname && *dft_ttyname)
 
221
    {
 
222
      if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
 
223
        return rc;
 
224
    }
 
225
 
 
226
  dft_ttytype = getenv ("TERM");
 
227
  if (dft_ttyname && dft_ttytype)
 
228
    {
 
229
      if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
 
230
        return rc;
 
231
    }
 
232
 
 
233
#if defined(HAVE_SETLOCALE) 
 
234
  {
 
235
    char *old_lc = NULL;
 
236
    char *dft_lc = NULL;
 
237
 
 
238
#if defined(LC_CTYPE)
 
239
    old_lc = setlocale (LC_CTYPE, NULL);
 
240
    if (old_lc)
 
241
      {
 
242
        char *p = spwq_malloc (strlen (old_lc)+1);
 
243
        if (!p)
 
244
          return SPWQ_OUT_OF_CORE;
 
245
        strcpy (p, old_lc);
 
246
        old_lc = p;
 
247
      }
 
248
    dft_lc = setlocale (LC_CTYPE, "");
 
249
    if (dft_ttyname && dft_lc)
 
250
      rc = agent_send_option (fd, "lc-ctype", dft_lc);
 
251
    if (old_lc)
 
252
      {
 
253
        setlocale (LC_CTYPE, old_lc);
 
254
        spwq_free (old_lc);
 
255
      }
 
256
    if (rc)
 
257
      return rc;
 
258
#endif
 
259
 
 
260
#if defined(LC_MESSAGES)
 
261
    old_lc = setlocale (LC_MESSAGES, NULL);
 
262
    if (old_lc)
 
263
      {
 
264
        char *p = spwq_malloc (strlen (old_lc)+1);
 
265
        if (!p)
 
266
          return SPWQ_OUT_OF_CORE;
 
267
        strcpy (p, old_lc);
 
268
        old_lc = p;
 
269
      }
 
270
    dft_lc = setlocale (LC_MESSAGES, "");
 
271
    if (dft_ttyname && dft_lc)
 
272
      rc = agent_send_option (fd, "lc-messages", dft_lc);
 
273
    if (old_lc)
 
274
      {
 
275
        setlocale (LC_MESSAGES, old_lc);
 
276
        spwq_free (old_lc);
 
277
      }
 
278
    if (rc)
 
279
      return rc;
 
280
#endif
 
281
  }
 
282
#endif /*HAVE_SETLOCALE*/
 
283
 
 
284
  return 0;
 
285
}
 
286
 
 
287
 
 
288
 
 
289
/* Try to open a connection to the agent, send all options and return
 
290
   the file descriptor for the connection.  Return -1 in case of
 
291
   error. */
 
292
static int
 
293
agent_open (int *rfd)
 
294
{
 
295
  int rc;
 
296
  int fd;
 
297
  char *infostr, *p;
 
298
  struct sockaddr_un client_addr;
 
299
  size_t len;
 
300
  int prot;
 
301
  char line[200];
 
302
  int nread;
 
303
 
 
304
  *rfd = -1;
 
305
  infostr = getenv ( "GPG_AGENT_INFO" );
 
306
  if ( !infostr || !*infostr ) 
 
307
    {
 
308
#ifdef SPWQ_USE_LOGGING
 
309
      log_error (_("gpg-agent is not available in this session\n"));
 
310
#endif
 
311
      return SPWQ_NO_AGENT;
 
312
    }
 
313
  p = spwq_malloc (strlen (infostr)+1);
 
314
  if (!p)
 
315
    return SPWQ_OUT_OF_CORE;
 
316
  strcpy (p, infostr);
 
317
  infostr = p;
 
318
 
 
319
  if ( !(p = strchr ( infostr, PATHSEP_C)) || p == infostr
 
320
       || (p-infostr)+1 >= sizeof client_addr.sun_path ) 
 
321
    {
 
322
#ifdef SPWQ_USE_LOGGING
 
323
      log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
 
324
#endif
 
325
      return SPWQ_NO_AGENT;
 
326
    }
 
327
  *p++ = 0;
 
328
 
 
329
  while (*p && *p != PATHSEP_C)
 
330
    p++;
 
331
  prot = *p? atoi (p+1) : 0;
 
332
  if ( prot != 1)
 
333
    {
 
334
#ifdef SPWQ_USE_LOGGING
 
335
      log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
 
336
#endif
 
337
      return SPWQ_PROTOCOL_ERROR;
 
338
    }
 
339
 
 
340
#ifdef HAVE_W32_SYSTEM       
 
341
  fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
 
342
#else
 
343
  fd = socket (AF_UNIX, SOCK_STREAM, 0);
 
344
#endif
 
345
  if (fd == -1) 
 
346
    {
 
347
#ifdef SPWQ_USE_LOGGING
 
348
      log_error ("can't create socket: %s\n", strerror(errno) );
 
349
#endif
 
350
      return SPWQ_SYS_ERROR;
 
351
    }
 
352
    
 
353
  memset (&client_addr, 0, sizeof client_addr);
 
354
  client_addr.sun_family = AF_UNIX;
 
355
  strcpy (client_addr.sun_path, infostr);
 
356
  len = (offsetof (struct sockaddr_un, sun_path)
 
357
         + strlen(client_addr.sun_path) + 1);
 
358
    
 
359
#ifdef HAVE_W32_SYSTEM       
 
360
  rc = _w32_sock_connect (fd, (struct sockaddr*)&client_addr, len );
 
361
#else
 
362
  rc = connect (fd, (struct sockaddr*)&client_addr, len );
 
363
#endif
 
364
  if (rc == -1)
 
365
    {
 
366
#ifdef SPWQ_USE_LOGGING
 
367
      log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
 
368
#endif
 
369
      close (fd );
 
370
      return SPWQ_IO_ERROR;
 
371
    }
 
372
 
 
373
  nread = readline (fd, line, DIM(line));
 
374
  if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
 
375
                     && (line[2] == '\n' || line[2] == ' ')) ) 
 
376
    {
 
377
#ifdef SPWQ_USE_LOGGING
 
378
      log_error ( _("communication problem with gpg-agent\n"));
 
379
#endif
 
380
      close (fd );
 
381
      return SPWQ_PROTOCOL_ERROR;
 
382
    }
 
383
 
 
384
  rc = agent_send_all_options (fd);
 
385
  if (rc)
 
386
    {
 
387
#ifdef SPWQ_USE_LOGGING
 
388
      log_error (_("problem setting the gpg-agent options\n"));
 
389
#endif
 
390
      close (fd);
 
391
      return rc;
 
392
    }
 
393
 
 
394
  *rfd = fd;
 
395
  return 0;
 
396
}
 
397
 
 
398
 
 
399
/* Copy text to BUFFER and escape as required.  Return a pointer to
 
400
   the end of the new buffer.  Note that BUFFER must be large enough
 
401
   to keep the entire text; allocataing it 3 times the size of TEXT
 
402
   is sufficient. */
 
403
static char *
 
404
copy_and_escape (char *buffer, const char *text)
 
405
{
 
406
  int i;
 
407
  const unsigned char *s = text;
 
408
  char *p = buffer;
 
409
  
 
410
 
 
411
  for (i=0; s[i]; i++)
 
412
    {
 
413
      if (s[i] < ' ' || s[i] == '+')
 
414
        {
 
415
          sprintf (p, "%%%02X", s[i]);
 
416
          p += 3;
 
417
        }
 
418
      else if (s[i] == ' ')
 
419
        *p++ = '+';
 
420
      else
 
421
        *p++ = s[i];
 
422
    }
 
423
  return p;
 
424
}
 
425
 
 
426
 
 
427
/* Ask the gpg-agent for a passphrase and present the user with a
 
428
   DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
 
429
   If a CACHEID is not NULL it is used to locate the passphrase in in
 
430
   the cache and store it under this ID.  If ERRORCODE is not NULL it
 
431
   should point a variable receiving an errorcode; thsi errocode might
 
432
   be 0 if the user canceled the operation.  The function returns NULL
 
433
   to indicate an error. */
 
434
char *
 
435
simple_pwquery (const char *cacheid, 
 
436
                const char *tryagain,
 
437
                const char *prompt,
 
438
                const char *description,
 
439
                int *errorcode)
 
440
{
 
441
  int fd = -1;
 
442
  int nread;
 
443
  char *result = NULL;
 
444
  char *pw = NULL;
 
445
  char *p;
 
446
  int rc, i; 
 
447
 
 
448
  rc = agent_open (&fd);
 
449
  if (rc)
 
450
    goto leave;
 
451
 
 
452
  if (!cacheid)
 
453
    cacheid = "X";
 
454
  if (!tryagain)
 
455
    tryagain = "X";
 
456
  if (!prompt)
 
457
    prompt = "X";
 
458
  if (!description)
 
459
    description = "X";
 
460
 
 
461
  {
 
462
    char *line;
 
463
    /* We allocate 3 times the needed space so that there is enough
 
464
       space for escaping. */
 
465
    line = spwq_malloc (15
 
466
                        + 3*strlen (cacheid) + 1
 
467
                        + 3*strlen (tryagain) + 1
 
468
                        + 3*strlen (prompt) + 1
 
469
                        + 3*strlen (description) + 1
 
470
                        + 2);
 
471
    if (!line)
 
472
      {
 
473
        rc = SPWQ_OUT_OF_CORE;
 
474
        goto leave;
 
475
      }
 
476
    strcpy (line, "GET_PASSPHRASE ");
 
477
    p = line+15;
 
478
    p = copy_and_escape (p, cacheid);
 
479
    *p++ = ' ';
 
480
    p = copy_and_escape (p, tryagain);
 
481
    *p++ = ' ';
 
482
    p = copy_and_escape (p, prompt);
 
483
    *p++ = ' ';
 
484
    p = copy_and_escape (p, description);
 
485
    *p++ = '\n';
 
486
    rc = writen (fd, line, p - line);
 
487
    spwq_free (line);
 
488
    if (rc)
 
489
      goto leave;
 
490
  }
 
491
 
 
492
  /* get response */
 
493
  pw = spwq_secure_malloc (500);
 
494
  nread = readline (fd, pw, 499);
 
495
  if (nread < 0)
 
496
    {
 
497
      rc = -nread;
 
498
      goto leave;
 
499
    }
 
500
  if (nread < 3)
 
501
    {
 
502
      rc = SPWQ_PROTOCOL_ERROR;
 
503
      goto leave;
 
504
    }
 
505
      
 
506
  if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
 
507
    { /* we got a passphrase - convert it back from hex */
 
508
      size_t pwlen = 0;
 
509
      
 
510
      for (i=3; i < nread && hexdigitp (pw+i); i+=2)
 
511
        pw[pwlen++] = xtoi_2 (pw+i);
 
512
      pw[pwlen] = 0; /* make a C String */
 
513
      result = pw;
 
514
      pw = NULL;
 
515
    }
 
516
  else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
 
517
            && (pw[7] == ' ' || pw[7] == '\n') )
 
518
           || ((nread > 4 && !memcmp (pw, "ERR ", 4)
 
519
                && (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) ) 
 
520
    {
 
521
      /* 111 is the old Assuan code for canceled which might still
 
522
         be in use by old installations. 99 is GPG_ERR_CANCELED as
 
523
         used by modern gpg-agents; 0xffff is used to mask out the
 
524
         error source.  */
 
525
#ifdef SPWQ_USE_LOGGING
 
526
      log_info (_("canceled by user\n") );
 
527
#endif
 
528
      *errorcode = 0; /* canceled */
 
529
    }
 
530
  else 
 
531
    {
 
532
#ifdef SPWQ_USE_LOGGING
 
533
      log_error (_("problem with the agent\n"));
 
534
#endif
 
535
      rc = SPWQ_ERR_RESPONSE;
 
536
    }
 
537
        
 
538
 leave:
 
539
  if (errorcode)
 
540
    *errorcode = rc;
 
541
  if (fd != -1)
 
542
    close (fd);
 
543
  if (pw)
 
544
    spwq_free (pw);
 
545
  return result;
 
546
}
 
547
 
 
548
 
 
549
/* Perform the simple query QUERY (which must be new-line and 0
 
550
   terminated) and return the error code.  */
 
551
int
 
552
simple_query (const char *query)
 
553
{
 
554
  int fd = -1;
 
555
  int nread;
 
556
  char response[500];
 
557
  int rc;
 
558
 
 
559
  rc = agent_open (&fd);
 
560
  if (rc)
 
561
    goto leave;
 
562
 
 
563
  rc = writen (fd, query, strlen (query));
 
564
  if (rc)
 
565
    goto leave;
 
566
 
 
567
  /* get response */
 
568
  nread = readline (fd, response, 499);
 
569
  if (nread < 0)
 
570
    {
 
571
      rc = -nread;
 
572
      goto leave;
 
573
    }
 
574
  if (nread < 3)
 
575
    {
 
576
      rc = SPWQ_PROTOCOL_ERROR;
 
577
      goto leave;
 
578
    }
 
579
      
 
580
  if (response[0] == 'O' && response[1] == 'K') 
 
581
    /* OK, do nothing.  */;
 
582
  else if ((nread > 7 && !memcmp (response, "ERR 111", 7)
 
583
            && (response[7] == ' ' || response[7] == '\n') )
 
584
           || ((nread > 4 && !memcmp (response, "ERR ", 4)
 
585
                && (strtoul (response+4, NULL, 0) & 0xffff) == 99)) ) 
 
586
    {
 
587
      /* 111 is the old Assuan code for canceled which might still
 
588
         be in use by old installations. 99 is GPG_ERR_CANCELED as
 
589
         used by modern gpg-agents; 0xffff is used to mask out the
 
590
         error source.  */
 
591
#ifdef SPWQ_USE_LOGGING
 
592
      log_info (_("canceled by user\n") );
 
593
#endif
 
594
    }
 
595
  else 
 
596
    {
 
597
#ifdef SPWQ_USE_LOGGING
 
598
      log_error (_("problem with the agent\n"));
 
599
#endif
 
600
      rc = SPWQ_ERR_RESPONSE;
 
601
    }
 
602
        
 
603
 leave:
 
604
  if (fd != -1)
 
605
    close (fd);
 
606
  return rc;
 
607
}