File: getpin-cb.c

package info (click to toggle)
poldi 0.4.1-3
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,892 kB
  • ctags: 1,112
  • sloc: ansic: 9,554; sh: 4,684; makefile: 245; sed: 16
file content (278 lines) | stat: -rw-r--r-- 7,036 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/* getpin-cb.c - getpin Assuan Callback (Poldi)
   Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH
 
   This file is part of Poldi.
 
   Poldi is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
 
   Poldi is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, see
   <http://www.gnu.org/licenses/>.  */

#include <poldi.h>

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <pwd.h>
#include <dirent.h>
#include <time.h>

#include <gcrypt.h>

#include "assuan.h"
#include "util/support.h"
#include <util/defs.h>
#include "util/util.h"
#include "util/simplelog.h"
#include "auth-support/conv.h"

#include "ctx.h"

#include "getpin-cb.h"



/* Returns TRUE if the string S contains only decimal digits, FALSE
   otherwise. */
static int
all_digitsp (const char *s)
{
  for (; *s && *s >= '0' && *s <= '9'; s++)
    ;
  return !*s;
}  

/* Query the user through PAM for his PIN.  Display INFO to the user.
   Store the retrieved pin in PIN, which is of size PIN_SIZE.  If it
   does not fit, return error. */
static int
query_user (poldi_ctx_t ctx, const char *info, char *pin, size_t pin_size)
{
  char *buffer;
  int rc;

  buffer = NULL;
  rc = 0;

  while (1)			/* Loop until well-formed PIN retrieved. */
    {
      /* Retrieve PIN through PAM.  */
      rc = conv_ask (ctx->conv, 1, &buffer, info);
      if (rc)
	goto out;

      /* Do some basic checks on the entered PIN. FIXME: hard-coded
	 values! Is this really the correct place for these checks?
	 Shouldn't they be done in scdaemon itself?  -mo */

      if (strlen (buffer) < 6)	/* FIXME? is it really minimum of 6 bytes? */
	{
	  log_msg_error (ctx->loghandle, _("PIN too short"));
	  conv_tell(ctx->conv, "%s", _("PIN too short"));
	}
/*       else if (!all_digitsp (buffer)) */
/* 	{ */
/* 	  log_msg_error (ctx->loghandle, _("invalid characters in PIN")); */
/* 	  conv_tell(ctx->conv, "%s", _("invalid characters in PIN")); */
/* 	} */
      else
	break;
    }

  if (strlen (buffer) >= pin_size)
    {
      log_msg_error (ctx->loghandle, _("PIN too long for buffer!"));
      rc = gpg_error (GPG_ERR_INV_DATA); /* ? */
      goto out;
    }

  strncpy (pin, buffer, pin_size - 1);
  pin[pin_size-1] = 0;

 out:

  return rc;
}

/* Pop up a message window similar to the confirm one but keep it open
   until agent_popup_message_stop has been called.  It is crucial for
   the caller to make sure that the stop function gets called as soon
   as the message is not anymore required because the message is
   system modal and all other attempts to use the pinentry will fail
   (after a timeout). */
static int
keypad_mode_enter (poldi_ctx_t ctx, const char *info)
{
  int rc;

  rc = conv_tell (ctx->conv, info);

  return rc;
}

static int
keypad_mode_leave (poldi_ctx_t ctx)
{
  return 0;
}

/* This function is taken from pinentry.c.  */
/* Note, that it is sufficient to allocate the target string D as
   long as the source string S, i.e.: strlen(s)+1; */
static void
strcpy_escaped (char *d, const unsigned char *s)
{
  while (*s)
    {
      if (*s == '%' && s[1] && s[2])
        { 
          s++;
          *d++ = xtoi_2 ( s);
          s += 2;
        }
      else
        *d++ = *s++;
    }
  *d = 0; 
}

/* Unescape special characters in INFO and write unescaped string into
   newly allocated memory in *INFO_FROBBED.  Returns proper error
   code.  */
static gpg_error_t
frob_info_msg (const char *info, char **info_frobbed)
{
  gpg_error_t err = 0;

  *info_frobbed = xtrymalloc (strlen (info) + 1);
  if (!*info_frobbed)
    {
      err = gpg_error_from_errno (errno);
      goto out;
    }

  strcpy_escaped (*info_frobbed, info);

 out:

  return err;
}

/* Callback used to ask for the PIN which shall be written into BUF.
   The buf has been allocated by the caller and is of size MAXBUF
   which includes the terminating null.  The function should return an
   UTF-8 string with the passphrase/PIN, the buffer may optionally be
   padded with arbitrary characters.

   INFO gets displayed as part of a generic string.  However if the
   first character of INFO is a vertical bar all up to the next
   verical bar are considered flags and only everything after the
   second vertical bar gets displayed as the full prompt.

   We don't need/implement the N/A flags.  When they occur, we signal
   an error.  */
int 
getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
{
  struct getpin_cb_data *cb_data = opaque;
  poldi_ctx_t ctx = cb_data->poldi_ctx;
  char *info_frobbed;
  int err;

  info_frobbed = NULL;
  err = 0;

#if 0
  /* FIXME: why "< 2"? -mo */
  if (buf && maxbuf < 2)
    return gpg_error (GPG_ERR_INV_VALUE);
#endif

  /* Older SCDaemons simply send "PIN" as prompt. We do not process
     this prompt here but use a special case later. */
  if (info && (strcmp (info, "PIN") != 0))
    {
      if (info[0] == '|')
	{
	  if (info[1] == '|')
	    /* Skip "||" at the beginning.  */
	    info += 2;
	  else
	    {
	      /* Weird that we received flags - they are neither expected nor
		 implemented here.  */
	      log_msg_error (ctx->loghandle,
			     _("getpin_cb called with flags set in info string `%s'\n"),
			     info);
	      err = gpg_error (GPG_ERR_INV_VALUE); /* FIXME? */
	      goto out;
	    }
	}
      err = frob_info_msg (info, &info_frobbed);
      if (err)
	{
	  log_msg_error (ctx->loghandle,
			 _("frob_info_msg failed for info msg of size of size %u\n"),
			 (unsigned int) strlen (info));
	  goto out;
	}
    }

  if (buf)
    {
      /* BUF being non-zero means we are not using a keypad.  */

      if (info_frobbed)
	err = query_user (ctx, info_frobbed, buf, maxbuf);
      else
	/* Use string which is more user friendly. */
	err = query_user (ctx, _("Please enter the PIN: "), buf, maxbuf);
    }
  else
    {
      /* Special handling for keypad mode hack. */

      /* If BUF has been passed as NULL, we are in keypad mode: the
	 callback notifies the user and immediately returns.  */
      if (maxbuf == 0)
	{
	  /* Close the "pinentry". */
	  err = keypad_mode_leave (ctx);
	}
      else if (maxbuf == 1)
	{
	  /* Open the "pinentry". */
	  if (info_frobbed)
	    err = keypad_mode_enter (ctx, info_frobbed);
	  else
	    err = keypad_mode_enter (ctx, _("Please enter the PIN: "));
	}
      else
        err = gpg_error (GPG_ERR_INV_VALUE); /* FIXME: must signal
						internal error(!)?
						-mo */
    }

 out:

  xfree (info_frobbed);

  return err;
}

/* END */