15b0a1ceaf3e7dbd4580c10d73a8c658efec8887
   1/*
   2 * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
   3 *               2012 Philipp A. Hartmann <pah@qo.cx>
   4 *
   5 *  This program is free software; you can redistribute it and/or modify
   6 *  it under the terms of the GNU General Public License as published by
   7 *  the Free Software Foundation; either version 2 of the License, or
   8 *  (at your option) any later version.
   9 *
  10 *  This program is distributed in the hope that it will be useful,
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 *  GNU General Public License for more details.
  14 *
  15 *  You should have received a copy of the GNU General Public License
  16 *  along with this program; if not, write to the Free Software
  17 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18 */
  19
  20/*
  21 * Credits:
  22 * - GNOME Keyring API handling originally written by John Szakmeister
  23 * - ported to credential helper API by Philipp A. Hartmann
  24 */
  25
  26#include <stdio.h>
  27#include <string.h>
  28#include <stdarg.h>
  29#include <stdlib.h>
  30#include <errno.h>
  31#include <gnome-keyring.h>
  32
  33/*
  34 * This credential struct and API is simplified from git's credential.{h,c}
  35 */
  36struct credential
  37{
  38        char          *protocol;
  39        char          *host;
  40        unsigned short port;
  41        char          *path;
  42        char          *username;
  43        char          *password;
  44};
  45
  46#define CREDENTIAL_INIT \
  47  { NULL,NULL,0,NULL,NULL,NULL }
  48
  49typedef int (*credential_op_cb)(struct credential*);
  50
  51struct credential_operation
  52{
  53        char             *name;
  54        credential_op_cb op;
  55};
  56
  57#define CREDENTIAL_OP_END \
  58  { NULL,NULL }
  59
  60/* ---------------- common helper functions ----------------- */
  61
  62static inline void free_password(char *password)
  63{
  64        char *c = password;
  65        if (!password)
  66                return;
  67
  68        while (*c) *c++ = '\0';
  69        free(password);
  70}
  71
  72static inline void warning(const char *fmt, ...)
  73{
  74        va_list ap;
  75
  76        va_start(ap, fmt);
  77        fprintf(stderr, "warning: ");
  78        vfprintf(stderr, fmt, ap);
  79        fprintf(stderr, "\n" );
  80        va_end(ap);
  81}
  82
  83static inline void error(const char *fmt, ...)
  84{
  85        va_list ap;
  86
  87        va_start(ap, fmt);
  88        fprintf(stderr, "error: ");
  89        vfprintf(stderr, fmt, ap);
  90        fprintf(stderr, "\n" );
  91        va_end(ap);
  92}
  93
  94static inline void die(const char *fmt, ...)
  95{
  96        va_list ap;
  97
  98        va_start(ap,fmt);
  99        error(fmt, ap);
 100        va_end(ap);
 101        exit(EXIT_FAILURE);
 102}
 103
 104static inline void die_errno(int err)
 105{
 106        error("%s", strerror(err));
 107        exit(EXIT_FAILURE);
 108}
 109
 110static inline char *xstrdup(const char *str)
 111{
 112        char *ret = strdup(str);
 113        if (!ret)
 114                die_errno(errno);
 115
 116        return ret;
 117}
 118
 119/* ----------------- GNOME Keyring functions ----------------- */
 120
 121/* create a special keyring option string, if path is given */
 122static char* keyring_object(struct credential *c)
 123{
 124        char* object = NULL;
 125
 126        if (!c->path)
 127                return object;
 128
 129        object = (char*) malloc(strlen(c->host)+strlen(c->path)+8);
 130        if(!object)
 131                die_errno(errno);
 132
 133        if(c->port)
 134                sprintf(object,"%s:%hd/%s",c->host,c->port,c->path);
 135        else
 136                sprintf(object,"%s/%s",c->host,c->path);
 137
 138        return object;
 139}
 140
 141int keyring_get(struct credential *c)
 142{
 143        char* object = NULL;
 144        GList *entries;
 145        GnomeKeyringNetworkPasswordData *password_data;
 146        GnomeKeyringResult result;
 147
 148        if (!c->protocol || !(c->host || c->path))
 149                return EXIT_FAILURE;
 150
 151        object = keyring_object(c);
 152
 153        result = gnome_keyring_find_network_password_sync(
 154                                c->username,
 155                                NULL /* domain */,
 156                                c->host,
 157                                object,
 158                                c->protocol,
 159                                NULL /* authtype */,
 160                                c->port,
 161                                &entries);
 162
 163        free(object);
 164
 165        if (result == GNOME_KEYRING_RESULT_NO_MATCH)
 166                return EXIT_SUCCESS;
 167
 168        if (result == GNOME_KEYRING_RESULT_CANCELLED)
 169                return EXIT_SUCCESS;
 170
 171        if (result != GNOME_KEYRING_RESULT_OK) {
 172                error("%s",gnome_keyring_result_to_message(result));
 173                return EXIT_FAILURE;
 174        }
 175
 176        /* pick the first one from the list */
 177        password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
 178
 179        free_password(c->password);
 180        c->password = xstrdup(password_data->password);
 181
 182        if (!c->username)
 183                c->username = xstrdup(password_data->user);
 184
 185        gnome_keyring_network_password_list_free(entries);
 186
 187        return EXIT_SUCCESS;
 188}
 189
 190
 191int keyring_store(struct credential *c)
 192{
 193        guint32 item_id;
 194        char  *object = NULL;
 195
 196        /*
 197         * Sanity check that what we are storing is actually sensible.
 198         * In particular, we can't make a URL without a protocol field.
 199         * Without either a host or pathname (depending on the scheme),
 200         * we have no primary key. And without a username and password,
 201         * we are not actually storing a credential.
 202         */
 203        if (!c->protocol || !(c->host || c->path) ||
 204            !c->username || !c->password)
 205                return EXIT_FAILURE;
 206
 207        object = keyring_object(c);
 208
 209        gnome_keyring_set_network_password_sync(
 210                                GNOME_KEYRING_DEFAULT,
 211                                c->username,
 212                                NULL /* domain */,
 213                                c->host,
 214                                object,
 215                                c->protocol,
 216                                NULL /* authtype */,
 217                                c->port,
 218                                c->password,
 219                                &item_id);
 220
 221        free(object);
 222        return EXIT_SUCCESS;
 223}
 224
 225int keyring_erase(struct credential *c)
 226{
 227        char  *object = NULL;
 228        GList *entries;
 229        GnomeKeyringNetworkPasswordData *password_data;
 230        GnomeKeyringResult result;
 231
 232        /*
 233         * Sanity check that we actually have something to match
 234         * against. The input we get is a restrictive pattern,
 235         * so technically a blank credential means "erase everything".
 236         * But it is too easy to accidentally send this, since it is equivalent
 237         * to empty input. So explicitly disallow it, and require that the
 238         * pattern have some actual content to match.
 239         */
 240        if (!c->protocol && !c->host && !c->path && !c->username)
 241                return EXIT_FAILURE;
 242
 243        object = keyring_object(c);
 244
 245        result = gnome_keyring_find_network_password_sync(
 246                                c->username,
 247                                NULL /* domain */,
 248                                c->host,
 249                                object,
 250                                c->protocol,
 251                                NULL /* authtype */,
 252                                c->port,
 253                                &entries);
 254
 255        free(object);
 256
 257        if (result == GNOME_KEYRING_RESULT_NO_MATCH)
 258                return EXIT_SUCCESS;
 259
 260        if (result == GNOME_KEYRING_RESULT_CANCELLED)
 261                return EXIT_SUCCESS;
 262
 263        if (result != GNOME_KEYRING_RESULT_OK)
 264        {
 265                error("%s",gnome_keyring_result_to_message(result));
 266                return EXIT_FAILURE;
 267        }
 268
 269        /* pick the first one from the list (delete all matches?) */
 270        password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
 271
 272        result = gnome_keyring_item_delete_sync(
 273                password_data->keyring, password_data->item_id);
 274
 275        gnome_keyring_network_password_list_free(entries);
 276
 277        if (result != GNOME_KEYRING_RESULT_OK)
 278        {
 279                error("%s",gnome_keyring_result_to_message(result));
 280                return EXIT_FAILURE;
 281        }
 282
 283        return EXIT_SUCCESS;
 284}
 285
 286/*
 287 * Table with helper operation callbacks, used by generic
 288 * credential helper main function.
 289 */
 290struct credential_operation const credential_helper_ops[] =
 291{
 292        { "get",   keyring_get   },
 293        { "store", keyring_store },
 294        { "erase", keyring_erase },
 295        CREDENTIAL_OP_END
 296};
 297
 298/* ------------------ credential functions ------------------ */
 299
 300void credential_init(struct credential *c)
 301{
 302        memset(c, 0, sizeof(*c));
 303}
 304
 305void credential_clear(struct credential *c)
 306{
 307        free(c->protocol);
 308        free(c->host);
 309        free(c->path);
 310        free(c->username);
 311        free_password(c->password);
 312
 313        credential_init(c);
 314}
 315
 316int credential_read(struct credential *c)
 317{
 318        char    buf[1024];
 319        ssize_t line_len = 0;
 320        char   *key      = buf;
 321        char   *value;
 322
 323        while (fgets(buf, sizeof(buf), stdin))
 324        {
 325                line_len = strlen(buf);
 326
 327                if(buf[line_len-1]=='\n')
 328                        buf[--line_len]='\0';
 329
 330                if(!line_len)
 331                        break;
 332
 333                value = strchr(buf,'=');
 334                if(!value) {
 335                        warning("invalid credential line: %s", key);
 336                        return -1;
 337                }
 338                *value++ = '\0';
 339
 340                if (!strcmp(key, "protocol")) {
 341                        free(c->protocol);
 342                        c->protocol = xstrdup(value);
 343                } else if (!strcmp(key, "host")) {
 344                        free(c->host);
 345                        c->host = xstrdup(value);
 346                        value = strrchr(c->host,':');
 347                        if (value) {
 348                                *value++ = '\0';
 349                                c->port = atoi(value);
 350                        }
 351                } else if (!strcmp(key, "path")) {
 352                        free(c->path);
 353                        c->path = xstrdup(value);
 354                } else if (!strcmp(key, "username")) {
 355                        free(c->username);
 356                        c->username = xstrdup(value);
 357                } else if (!strcmp(key, "password")) {
 358                        free_password(c->password);
 359                        c->password = xstrdup(value);
 360                        while (*value) *value++ = '\0';
 361                }
 362                /*
 363                 * Ignore other lines; we don't know what they mean, but
 364                 * this future-proofs us when later versions of git do
 365                 * learn new lines, and the helpers are updated to match.
 366                 */
 367        }
 368        return 0;
 369}
 370
 371void credential_write_item(FILE *fp, const char *key, const char *value)
 372{
 373        if (!value)
 374                return;
 375        fprintf(fp, "%s=%s\n", key, value);
 376}
 377
 378void credential_write(const struct credential *c)
 379{
 380        /* only write username/password, if set */
 381        credential_write_item(stdout, "username", c->username);
 382        credential_write_item(stdout, "password", c->password);
 383}
 384
 385static void usage(const char *name)
 386{
 387        struct credential_operation const *try_op = credential_helper_ops;
 388        const char *basename = strrchr(name,'/');
 389
 390        basename = (basename) ? basename + 1 : name;
 391        fprintf(stderr, "usage: %s <", basename);
 392        while(try_op->name) {
 393                fprintf(stderr,"%s",(try_op++)->name);
 394                if(try_op->name)
 395                        fprintf(stderr,"%s","|");
 396        }
 397        fprintf(stderr,"%s",">\n");
 398}
 399
 400int main(int argc, char *argv[])
 401{
 402        int ret = EXIT_SUCCESS;
 403
 404        struct credential_operation const *try_op = credential_helper_ops;
 405        struct credential                  cred   = CREDENTIAL_INIT;
 406
 407        if (!argv[1]) {
 408                usage(argv[0]);
 409                goto out;
 410        }
 411
 412        /* lookup operation callback */
 413        while(try_op->name && strcmp(argv[1], try_op->name))
 414                try_op++;
 415
 416        /* unsupported operation given -- ignore silently */
 417        if(!try_op->name || !try_op->op)
 418                goto out;
 419
 420        ret = credential_read(&cred);
 421        if(ret)
 422                goto out;
 423
 424        /* perform credential operation */
 425        ret = (*try_op->op)(&cred);
 426
 427        credential_write(&cred);
 428
 429out:
 430        credential_clear(&cred);
 431        return ret;
 432}