]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/subversion/subversion/libsvn_subr/gpg_agent.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / contrib / subversion / subversion / libsvn_subr / gpg_agent.c
1 /*
2  * gpg_agent.c: GPG Agent provider for SVN_AUTH_CRED_*
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26 /* This auth provider stores a plaintext password in memory managed by
27  * a running gpg-agent. In contrast to other password store providers
28  * it does not save the password to disk.
29  *
30  * Prompting is performed by the gpg-agent using a "pinentry" program
31  * which needs to be installed separately. There are several pinentry
32  * implementations with different front-ends (e.g. qt, gtk, ncurses).
33  *
34  * The gpg-agent will let the password time out after a while,
35  * or immediately when it receives the SIGHUP signal.
36  * When the password has timed out it will automatically prompt the
37  * user for the password again. This is transparent to Subversion.
38  *
39  * SECURITY CONSIDERATIONS:
40  *
41  * Communication to the agent happens over a UNIX socket, which is located
42  * in a directory which only the user running Subversion can access.
43  * However, any program the user runs could access this socket and get
44  * the Subversion password if the program knows the "cache ID" Subversion
45  * uses for the password.
46  * The cache ID is very easy to obtain for programs running as the same user.
47  * Subversion uses the MD5 of the realmstring as cache ID, and these checksums
48  * are also used as filenames within ~/.subversion/auth/svn.simple.
49  * Unlike GNOME Keyring or KDE Wallet, the user is not prompted for
50  * permission if another program attempts to access the password.
51  *
52  * Therefore, while the gpg-agent is running and has the password cached,
53  * this provider is no more secure than a file storing the password in
54  * plaintext.
55  */
56
57 \f
58 /*** Includes. ***/
59
60 #ifndef WIN32
61
62 #include <unistd.h>
63
64 #include <sys/socket.h>
65 #include <sys/un.h>
66
67 #include <apr_pools.h>
68 #include "svn_auth.h"
69 #include "svn_config.h"
70 #include "svn_error.h"
71 #include "svn_pools.h"
72 #include "svn_cmdline.h"
73 #include "svn_checksum.h"
74 #include "svn_string.h"
75 #include "svn_hash.h"
76 #include "svn_user.h"
77 #include "svn_dirent_uri.h"
78
79 #include "private/svn_auth_private.h"
80
81 #include "svn_private_config.h"
82
83 #ifdef SVN_HAVE_GPG_AGENT
84
85 #define BUFFER_SIZE 1024
86 #define ATTEMPT_PARAMETER "svn.simple.gpg_agent.attempt"
87
88 /* Modify STR in-place such that blanks are escaped as required by the
89  * gpg-agent protocol. Return a pointer to STR. */
90 static char *
91 escape_blanks(char *str)
92 {
93   char *s = str;
94
95   while (*s)
96     {
97       if (*s == ' ')
98         *s = '+';
99       s++;
100     }
101
102   return str;
103 }
104
105 /* Generate the string CACHE_ID_P based on the REALMSTRING allocated in
106  * RESULT_POOL using SCRATCH_POOL for temporary allocations.  This is similar
107  * to other password caching mechanisms. */
108 static svn_error_t *
109 get_cache_id(const char **cache_id_p, const char *realmstring,
110              apr_pool_t *scratch_pool, apr_pool_t *result_pool)
111 {
112   const char *cache_id = NULL;
113   svn_checksum_t *digest = NULL;
114
115   SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring,
116                        strlen(realmstring), scratch_pool));
117   cache_id = svn_checksum_to_cstring(digest, result_pool);
118   *cache_id_p = cache_id;
119
120   return SVN_NO_ERROR;
121 }
122
123 /* Attempt to read a gpg-agent response message from the socket SD into
124  * buffer BUF. Buf is assumed to be N bytes large. Return TRUE if a response
125  * message could be read that fits into the buffer. Else return FALSE.
126  * If a message could be read it will always be NUL-terminated and the
127  * trailing newline is retained. */
128 static svn_boolean_t
129 receive_from_gpg_agent(int sd, char *buf, size_t n)
130 {
131   int i = 0;
132   size_t recvd;
133   char c;
134
135   /* Clear existing buffer content before reading response. */
136   if (n > 0)
137     *buf = '\0';
138
139   /* Require the message to fit into the buffer and be terminated
140    * with a newline. */
141   while (i < n)
142     {
143       recvd = read(sd, &c, 1);
144       if (recvd == -1)
145         return FALSE;
146       buf[i] = c;
147       i++;
148       if (i < n && c == '\n')
149         {
150           buf[i] = '\0';
151           return TRUE;
152         }
153     }
154
155     return FALSE;
156 }
157
158 /* Using socket SD, send the option OPTION with the specified VALUE
159  * to the gpg agent. Store the response in BUF, assumed to be N bytes
160  * in size, and evaluate the response. Return TRUE if the agent liked
161  * the smell of the option, if there is such a thing, and doesn't feel
162  * saturated by it. Else return FALSE.
163  * Do temporary allocations in scratch_pool. */
164 static svn_boolean_t
165 send_option(int sd, char *buf, size_t n, const char *option, const char *value,
166             apr_pool_t *scratch_pool)
167 {
168   const char *request;
169
170   request = apr_psprintf(scratch_pool, "OPTION %s=%s\n", option, value);
171
172   if (write(sd, request, strlen(request)) == -1)
173     return FALSE;
174
175   if (!receive_from_gpg_agent(sd, buf, n))
176     return FALSE;
177
178   return (strncmp(buf, "OK", 2) == 0);
179 }
180
181 /* Send the BYE command and disconnect from the gpg-agent.  Doing this avoids
182  * gpg-agent emitting a "Connection reset by peer" log message with some
183  * versions of gpg-agent. */
184 static void
185 bye_gpg_agent(int sd)
186 {
187   /* don't bother to check the result of the write, it either worked or it
188    * didn't, but either way we're closing. */
189   write(sd, "BYE\n", 4);
190   close(sd);
191 }
192
193 /* Locate a running GPG Agent, and return an open file descriptor
194  * for communication with the agent in *NEW_SD. If no running agent
195  * can be found, set *NEW_SD to -1. */
196 static svn_error_t *
197 find_running_gpg_agent(int *new_sd, apr_pool_t *pool)
198 {
199   char *buffer;
200   char *gpg_agent_info = NULL;
201   const char *socket_name = NULL;
202   const char *request = NULL;
203   const char *p = NULL;
204   char *ep = NULL;
205   int sd;
206
207   *new_sd = -1;
208
209   /* This implements the method of finding the socket as described in
210    * the gpg-agent man page under the --use-standard-socket option.
211    * The manage page misleadingly says the standard socket is 
212    * "named 'S.gpg-agent' located in the home directory."  The standard
213    * socket path is actually in the .gnupg directory in the home directory,
214    * i.e. ~/.gnupg/S.gpg-agent */
215   gpg_agent_info = getenv("GPG_AGENT_INFO");
216   if (gpg_agent_info != NULL)
217     {
218       apr_array_header_t *socket_details;
219
220       /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
221        * The path to the socket, the pid of the gpg-agent process and 
222        * finally the version of the protocol the agent talks. */
223       socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
224                                          pool);
225       socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
226     }
227   else
228     {
229       const char *homedir = svn_user_get_homedir(pool);
230
231       if (!homedir)
232         return SVN_NO_ERROR;
233
234       socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
235                                          "S.gpg-agent", NULL);
236     }
237
238   if (socket_name != NULL)
239     {
240       struct sockaddr_un addr;
241
242       addr.sun_family = AF_UNIX;
243       strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);
244       addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
245
246       sd = socket(AF_UNIX, SOCK_STREAM, 0);
247       if (sd == -1)
248         return SVN_NO_ERROR;
249
250       if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
251         {
252           close(sd);
253           return SVN_NO_ERROR;
254         }
255     }
256   else
257     return SVN_NO_ERROR;
258
259   /* Receive the connection status from the gpg-agent daemon. */
260   buffer = apr_palloc(pool, BUFFER_SIZE);
261   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
262     {
263       bye_gpg_agent(sd);
264       return SVN_NO_ERROR;
265     }
266
267   if (strncmp(buffer, "OK", 2) != 0)
268     {
269       bye_gpg_agent(sd);
270       return SVN_NO_ERROR;
271     }
272
273   /* The GPG-Agent documentation says:
274    *  "Clients should deny to access an agent with a socket name which does
275    *   not match its own configuration". */
276   request = "GETINFO socket_name\n";
277   if (write(sd, request, strlen(request)) == -1)
278     {
279       bye_gpg_agent(sd);
280       return SVN_NO_ERROR;
281     }
282   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
283     {
284       bye_gpg_agent(sd);
285       return SVN_NO_ERROR;
286     }
287   if (strncmp(buffer, "D", 1) == 0)
288     p = &buffer[2];
289   if (!p)
290     {
291       bye_gpg_agent(sd);
292       return SVN_NO_ERROR;
293     }
294   ep = strchr(p, '\n');
295   if (ep != NULL)
296     *ep = '\0';
297   if (strcmp(socket_name, p) != 0)
298     {
299       bye_gpg_agent(sd);
300       return SVN_NO_ERROR;
301     }
302   /* The agent will terminate its response with "OK". */
303   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
304     {
305       bye_gpg_agent(sd);
306       return SVN_NO_ERROR;
307     }
308   if (strncmp(buffer, "OK", 2) != 0)
309     {
310       bye_gpg_agent(sd);
311       return SVN_NO_ERROR;
312     }
313
314   *new_sd = sd;
315   return SVN_NO_ERROR;
316 }
317
318 static svn_boolean_t
319 send_options(int sd, char *buf, size_t n, apr_pool_t *scratch_pool)
320 {
321   const char *tty_name;
322   const char *tty_type;
323   const char *lc_ctype;
324   const char *display;
325
326   /* Send TTY_NAME to the gpg-agent daemon. */
327   tty_name = getenv("GPG_TTY");
328   if (tty_name != NULL)
329     {
330       if (!send_option(sd, buf, n, "ttyname", tty_name, scratch_pool))
331         return FALSE;
332     }
333
334   /* Send TTY_TYPE to the gpg-agent daemon. */
335   tty_type = getenv("TERM");
336   if (tty_type != NULL)
337     {
338       if (!send_option(sd, buf, n, "ttytype", tty_type, scratch_pool))
339         return FALSE;
340     }
341
342   /* Compute LC_CTYPE. */
343   lc_ctype = getenv("LC_ALL");
344   if (lc_ctype == NULL)
345     lc_ctype = getenv("LC_CTYPE");
346   if (lc_ctype == NULL)
347     lc_ctype = getenv("LANG");
348
349   /* Send LC_CTYPE to the gpg-agent daemon. */
350   if (lc_ctype != NULL)
351     {
352       if (!send_option(sd, buf, n, "lc-ctype", lc_ctype, scratch_pool))
353         return FALSE;
354     }
355
356   /* Send DISPLAY to the gpg-agent daemon. */
357   display = getenv("DISPLAY");
358   if (display != NULL)
359     {
360       if (!send_option(sd, buf, n, "display", display, scratch_pool))
361         return FALSE;
362     }
363
364   return TRUE;
365 }
366
367 /* Implementation of svn_auth__password_get_t that retrieves the password
368    from gpg-agent */
369 static svn_error_t *
370 password_get_gpg_agent(svn_boolean_t *done,
371                        const char **password,
372                        apr_hash_t *creds,
373                        const char *realmstring,
374                        const char *username,
375                        apr_hash_t *parameters,
376                        svn_boolean_t non_interactive,
377                        apr_pool_t *pool)
378 {
379   int sd;
380   const char *p = NULL;
381   char *ep = NULL;
382   char *buffer;
383   const char *request = NULL;
384   const char *cache_id = NULL;
385   char *password_prompt;
386   char *realm_prompt;
387   char *error_prompt;
388   int *attempt;
389
390   *done = FALSE;
391
392   attempt = svn_hash_gets(parameters, ATTEMPT_PARAMETER);
393
394   SVN_ERR(find_running_gpg_agent(&sd, pool));
395   if (sd == -1)
396     return SVN_NO_ERROR;
397
398   buffer = apr_palloc(pool, BUFFER_SIZE);
399
400   if (!send_options(sd, buffer, BUFFER_SIZE, pool))
401     {
402       bye_gpg_agent(sd);
403       return SVN_NO_ERROR;
404     }
405
406   SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool));
407
408   password_prompt = apr_psprintf(pool, _("Password for '%s': "), username);
409   realm_prompt = apr_psprintf(pool, _("Enter your Subversion password for %s"),
410                               realmstring);
411   if (*attempt == 1)
412     /* X means no error to the gpg-agent protocol */
413     error_prompt = apr_pstrdup(pool, "X");
414   else
415     error_prompt = apr_pstrdup(pool, _("Authentication failed"));
416
417   request = apr_psprintf(pool,
418                          "GET_PASSPHRASE --data %s"
419                          "%s %s %s %s\n",
420                          non_interactive ? "--no-ask " : "",
421                          cache_id,
422                          escape_blanks(error_prompt),
423                          escape_blanks(password_prompt),
424                          escape_blanks(realm_prompt));
425
426   if (write(sd, request, strlen(request)) == -1)
427     {
428       bye_gpg_agent(sd);
429       return SVN_NO_ERROR;
430     }
431   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
432     {
433       bye_gpg_agent(sd);
434       return SVN_NO_ERROR;
435     }
436
437   bye_gpg_agent(sd);
438
439   if (strncmp(buffer, "ERR", 3) == 0)
440     return SVN_NO_ERROR;
441
442   p = NULL;
443   if (strncmp(buffer, "D", 1) == 0)
444     p = &buffer[2];
445
446   if (!p)
447     return SVN_NO_ERROR;
448
449   ep = strchr(p, '\n');
450   if (ep != NULL)
451     *ep = '\0';
452
453   *password = p;
454
455   *done = TRUE;
456   return SVN_NO_ERROR;
457 }
458
459
460 /* Implementation of svn_auth__password_set_t that would store the
461    password in GPG Agent if that's how this particular integration
462    worked.  But it isn't.  GPG Agent stores the password provided by
463    the user via the pinentry program immediately upon its provision
464    (and regardless of its accuracy as passwords go), so we just need
465    to check if a running GPG Agent exists. */
466 static svn_error_t *
467 password_set_gpg_agent(svn_boolean_t *done,
468                        apr_hash_t *creds,
469                        const char *realmstring,
470                        const char *username,
471                        const char *password,
472                        apr_hash_t *parameters,
473                        svn_boolean_t non_interactive,
474                        apr_pool_t *pool)
475 {
476   int sd;
477
478   *done = FALSE;
479
480   SVN_ERR(find_running_gpg_agent(&sd, pool));
481   if (sd == -1)
482     return SVN_NO_ERROR;
483
484   bye_gpg_agent(sd);
485   *done = TRUE;
486
487   return SVN_NO_ERROR;
488 }
489
490
491 /* An implementation of svn_auth_provider_t::first_credentials() */
492 static svn_error_t *
493 simple_gpg_agent_first_creds(void **credentials,
494                              void **iter_baton,
495                              void *provider_baton,
496                              apr_hash_t *parameters,
497                              const char *realmstring,
498                              apr_pool_t *pool)
499 {
500   svn_error_t *err;
501   int *attempt = apr_palloc(pool, sizeof(*attempt));
502
503   *attempt = 1;
504   svn_hash_sets(parameters, ATTEMPT_PARAMETER, attempt);
505   err = svn_auth__simple_creds_cache_get(credentials, iter_baton,
506                                          provider_baton, parameters,
507                                          realmstring, password_get_gpg_agent,
508                                          SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
509                                          pool);
510   *iter_baton = attempt;
511
512   return err;
513 }
514
515 /* An implementation of svn_auth_provider_t::next_credentials() */
516 static svn_error_t *
517 simple_gpg_agent_next_creds(void **credentials,
518                             void *iter_baton,
519                             void *provider_baton,
520                             apr_hash_t *parameters,
521                             const char *realmstring,
522                             apr_pool_t *pool)
523 {
524   int *attempt = (int *)iter_baton;
525   int sd;
526   char *buffer;
527   const char *cache_id = NULL;
528   const char *request = NULL;
529
530   *credentials = NULL;
531
532   /* The users previous credentials failed so first remove the cached entry,
533    * before trying to retrieve them again.  Because gpg-agent stores cached
534    * credentials immediately upon retrieving them, this gives us the
535    * opportunity to remove the invalid credentials and prompt the
536    * user again.  While it's possible that server side issues could trigger
537    * this, this cache is ephemeral so at worst we're just speeding up
538    * when the user would need to re-enter their password. */
539
540   if (svn_hash_gets(parameters, SVN_AUTH_PARAM_NON_INTERACTIVE))
541     {
542       /* In this case since we're running non-interactively we do not
543        * want to clear the cache since the user was never prompted by
544        * gpg-agent to set a password. */
545       return SVN_NO_ERROR;
546     }
547
548   *attempt = *attempt + 1;
549
550   SVN_ERR(find_running_gpg_agent(&sd, pool));
551   if (sd == -1)
552     return SVN_NO_ERROR;
553
554   buffer = apr_palloc(pool, BUFFER_SIZE);
555
556   if (!send_options(sd, buffer, BUFFER_SIZE, pool))
557     {
558       bye_gpg_agent(sd);
559       return SVN_NO_ERROR;
560     }
561
562   SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool));
563
564   request = apr_psprintf(pool, "CLEAR_PASSPHRASE %s\n", cache_id);
565
566   if (write(sd, request, strlen(request)) == -1)
567     {
568       bye_gpg_agent(sd);
569       return SVN_NO_ERROR;
570     }
571
572   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
573     {
574       bye_gpg_agent(sd);
575       return SVN_NO_ERROR;
576     }
577
578   if (strncmp(buffer, "OK\n", 3) != 0)
579     {
580       bye_gpg_agent(sd);
581       return SVN_NO_ERROR;
582     }
583
584   /* TODO: This attempt limit hard codes it at 3 attempts (or 2 retries)
585    * which matches svn command line client's retry_limit as set in
586    * svn_cmdline_create_auth_baton().  It would be nice to have that
587    * limit reflected here but that violates the boundry between the
588    * prompt provider and the cache provider.  gpg-agent is acting as
589    * both here due to the peculiarties of their design so we'll have to
590    * live with this for now.  Note that when these failures get exceeded
591    * it'll eventually fall back on the retry limits of whatever prompt
592    * provider is in effect, so this effectively doubles the limit. */
593   if (*attempt < 4)
594     return svn_auth__simple_creds_cache_get(credentials, &iter_baton,
595                                             provider_baton, parameters,
596                                             realmstring,
597                                             password_get_gpg_agent,
598                                             SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
599                                             pool);
600
601   return SVN_NO_ERROR;
602 }
603
604
605 /* An implementation of svn_auth_provider_t::save_credentials() */
606 static svn_error_t *
607 simple_gpg_agent_save_creds(svn_boolean_t *saved,
608                             void *credentials,
609                             void *provider_baton,
610                             apr_hash_t *parameters,
611                             const char *realmstring,
612                             apr_pool_t *pool)
613 {
614   return svn_auth__simple_creds_cache_set(saved, credentials,
615                                           provider_baton, parameters,
616                                           realmstring, password_set_gpg_agent,
617                                           SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
618                                           pool);
619 }
620
621
622 static const svn_auth_provider_t gpg_agent_simple_provider = {
623   SVN_AUTH_CRED_SIMPLE,
624   simple_gpg_agent_first_creds,
625   simple_gpg_agent_next_creds,
626   simple_gpg_agent_save_creds
627 };
628
629
630 /* Public API */
631 void
632 svn_auth_get_gpg_agent_simple_provider(svn_auth_provider_object_t **provider,
633                                        apr_pool_t *pool)
634 {
635   svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
636
637   po->vtable = &gpg_agent_simple_provider;
638   *provider = po;
639 }
640
641 #endif /* SVN_HAVE_GPG_AGENT */
642 #endif /* !WIN32 */