~ubuntu-branches/ubuntu/hardy/postgresql-8.4/hardy-backports

« back to all changes in this revision

Viewing changes to src/interfaces/libpq/fe-auth.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-03-20 12:00:13 UTC
  • Revision ID: james.westby@ubuntu.com-20090320120013-hogj7egc5mjncc5g
Tags: upstream-8.4~0cvs20090328
ImportĀ upstreamĀ versionĀ 8.4~0cvs20090328

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * fe-auth.c
 
4
 *         The front-end (client) authorization routines
 
5
 *
 
6
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 * IDENTIFICATION
 
10
 *        $PostgreSQL$
 
11
 *
 
12
 *-------------------------------------------------------------------------
 
13
 */
 
14
 
 
15
/*
 
16
 * INTERFACE ROUTINES
 
17
 *         frontend (client) routines:
 
18
 *              pg_fe_sendauth                  send authentication information
 
19
 *              pg_fe_getauthname               get user's name according to the client side
 
20
 *                                                              of the authentication system
 
21
 */
 
22
 
 
23
#include "postgres_fe.h"
 
24
 
 
25
#ifdef WIN32
 
26
#include "win32.h"
 
27
#else
 
28
#include <unistd.h>
 
29
#include <fcntl.h>
 
30
#include <sys/types.h>
 
31
#include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
 
32
#include <sys/socket.h>
 
33
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
 
34
#include <sys/uio.h>
 
35
#include <sys/ucred.h>
 
36
#endif
 
37
#ifndef  MAXHOSTNAMELEN
 
38
#include <netdb.h>                              /* for MAXHOSTNAMELEN on some */
 
39
#endif
 
40
#include <pwd.h>
 
41
#endif
 
42
 
 
43
#include "libpq-fe.h"
 
44
#include "fe-auth.h"
 
45
#include "libpq/md5.h"
 
46
 
 
47
 
 
48
#ifdef KRB5
 
49
/*
 
50
 * MIT Kerberos authentication system - protocol version 5
 
51
 */
 
52
 
 
53
#include <krb5.h>
 
54
/* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
 
55
#if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
 
56
#include <com_err.h>
 
57
#endif
 
58
 
 
59
/*
 
60
 * Heimdal doesn't have a free function for unparsed names. Just pass it to
 
61
 * standard free() which should work in these cases.
 
62
 */
 
63
#ifndef HAVE_KRB5_FREE_UNPARSED_NAME
 
64
static void
 
65
krb5_free_unparsed_name(krb5_context context, char *val)
 
66
{
 
67
        free(val);
 
68
}
 
69
#endif
 
70
 
 
71
/*
 
72
 * pg_an_to_ln -- return the local name corresponding to an authentication
 
73
 *                                name
 
74
 *
 
75
 * XXX Assumes that the first aname component is the user name.  This is NOT
 
76
 *         necessarily so, since an aname can actually be something out of your
 
77
 *         worst X.400 nightmare, like
 
78
 *                ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
 
79
 *         Note that the MIT an_to_ln code does the same thing if you don't
 
80
 *         provide an aname mapping database...it may be a better idea to use
 
81
 *         krb5_an_to_ln, except that it punts if multiple components are found,
 
82
 *         and we can't afford to punt.
 
83
 *
 
84
 * For WIN32, convert username to lowercase because the Win32 kerberos library
 
85
 * generates tickets with the username as the user entered it instead of as
 
86
 * it is entered in the directory.
 
87
 */
 
88
static char *
 
89
pg_an_to_ln(char *aname)
 
90
{
 
91
        char       *p;
 
92
 
 
93
        if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
 
94
                *p = '\0';
 
95
#ifdef WIN32
 
96
        for (p = aname; *p; p++)
 
97
                *p = pg_tolower((unsigned char) *p);
 
98
#endif
 
99
 
 
100
        return aname;
 
101
}
 
102
 
 
103
 
 
104
/*
 
105
 * Various krb5 state which is not connection specific, and a flag to
 
106
 * indicate whether we have initialised it yet.
 
107
 */
 
108
/*
 
109
static int      pg_krb5_initialised;
 
110
static krb5_context pg_krb5_context;
 
111
static krb5_ccache pg_krb5_ccache;
 
112
static krb5_principal pg_krb5_client;
 
113
static char *pg_krb5_name;
 
114
*/
 
115
 
 
116
struct krb5_info
 
117
{
 
118
        int                     pg_krb5_initialised;
 
119
        krb5_context pg_krb5_context;
 
120
        krb5_ccache pg_krb5_ccache;
 
121
        krb5_principal pg_krb5_client;
 
122
        char       *pg_krb5_name;
 
123
};
 
124
 
 
125
 
 
126
static int
 
127
pg_krb5_init(PQExpBuffer errorMessage, struct krb5_info * info)
 
128
{
 
129
        krb5_error_code retval;
 
130
 
 
131
        if (info->pg_krb5_initialised)
 
132
                return STATUS_OK;
 
133
 
 
134
        retval = krb5_init_context(&(info->pg_krb5_context));
 
135
        if (retval)
 
136
        {
 
137
                printfPQExpBuffer(errorMessage,
 
138
                                                  "pg_krb5_init: krb5_init_context: %s\n",
 
139
                                                  error_message(retval));
 
140
                return STATUS_ERROR;
 
141
        }
 
142
 
 
143
        retval = krb5_cc_default(info->pg_krb5_context, &(info->pg_krb5_ccache));
 
144
        if (retval)
 
145
        {
 
146
                printfPQExpBuffer(errorMessage,
 
147
                                                  "pg_krb5_init: krb5_cc_default: %s\n",
 
148
                                                  error_message(retval));
 
149
                krb5_free_context(info->pg_krb5_context);
 
150
                return STATUS_ERROR;
 
151
        }
 
152
 
 
153
        retval = krb5_cc_get_principal(info->pg_krb5_context, info->pg_krb5_ccache,
 
154
                                                                   &(info->pg_krb5_client));
 
155
        if (retval)
 
156
        {
 
157
                printfPQExpBuffer(errorMessage,
 
158
                                                  "pg_krb5_init: krb5_cc_get_principal: %s\n",
 
159
                                                  error_message(retval));
 
160
                krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache);
 
161
                krb5_free_context(info->pg_krb5_context);
 
162
                return STATUS_ERROR;
 
163
        }
 
164
 
 
165
        retval = krb5_unparse_name(info->pg_krb5_context, info->pg_krb5_client, &(info->pg_krb5_name));
 
166
        if (retval)
 
167
        {
 
168
                printfPQExpBuffer(errorMessage,
 
169
                                                  "pg_krb5_init: krb5_unparse_name: %s\n",
 
170
                                                  error_message(retval));
 
171
                krb5_free_principal(info->pg_krb5_context, info->pg_krb5_client);
 
172
                krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache);
 
173
                krb5_free_context(info->pg_krb5_context);
 
174
                return STATUS_ERROR;
 
175
        }
 
176
 
 
177
        info->pg_krb5_name = pg_an_to_ln(info->pg_krb5_name);
 
178
 
 
179
        info->pg_krb5_initialised = 1;
 
180
        return STATUS_OK;
 
181
}
 
182
 
 
183
static void
 
184
pg_krb5_destroy(struct krb5_info * info)
 
185
{
 
186
        krb5_free_principal(info->pg_krb5_context, info->pg_krb5_client);
 
187
        krb5_cc_close(info->pg_krb5_context, info->pg_krb5_ccache);
 
188
        krb5_free_unparsed_name(info->pg_krb5_context, info->pg_krb5_name);
 
189
        krb5_free_context(info->pg_krb5_context);
 
190
}
 
191
 
 
192
 
 
193
/*
 
194
 * pg_krb5_sendauth -- client routine to send authentication information to
 
195
 *                                         the server
 
196
 */
 
197
static int
 
198
pg_krb5_sendauth(PGconn *conn)
 
199
{
 
200
        krb5_error_code retval;
 
201
        int                     ret;
 
202
        krb5_principal server;
 
203
        krb5_auth_context auth_context = NULL;
 
204
        krb5_error *err_ret = NULL;
 
205
        struct krb5_info info;
 
206
 
 
207
        info.pg_krb5_initialised = 0;
 
208
 
 
209
        if (!conn->pghost)
 
210
        {
 
211
                printfPQExpBuffer(&conn->errorMessage,
 
212
                                                  "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
 
213
                return STATUS_ERROR;
 
214
        }
 
215
 
 
216
        ret = pg_krb5_init(&conn->errorMessage, &info);
 
217
        if (ret != STATUS_OK)
 
218
                return ret;
 
219
 
 
220
        retval = krb5_sname_to_principal(info.pg_krb5_context, conn->pghost,
 
221
                                                                         conn->krbsrvname,
 
222
                                                                         KRB5_NT_SRV_HST, &server);
 
223
        if (retval)
 
224
        {
 
225
                printfPQExpBuffer(&conn->errorMessage,
 
226
                                                  "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
 
227
                                                  error_message(retval));
 
228
                pg_krb5_destroy(&info);
 
229
                return STATUS_ERROR;
 
230
        }
 
231
 
 
232
        /*
 
233
         * libpq uses a non-blocking socket. But kerberos needs a blocking socket,
 
234
         * and we have to block somehow to do mutual authentication anyway. So we
 
235
         * temporarily make it blocking.
 
236
         */
 
237
        if (!pg_set_block(conn->sock))
 
238
        {
 
239
                char            sebuf[256];
 
240
 
 
241
                printfPQExpBuffer(&conn->errorMessage,
 
242
                                                  libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf)));
 
243
                krb5_free_principal(info.pg_krb5_context, server);
 
244
                pg_krb5_destroy(&info);
 
245
                return STATUS_ERROR;
 
246
        }
 
247
 
 
248
        retval = krb5_sendauth(info.pg_krb5_context, &auth_context,
 
249
                                          (krb5_pointer) & conn->sock, (char *) conn->krbsrvname,
 
250
                                                   info.pg_krb5_client, server,
 
251
                                                   AP_OPTS_MUTUAL_REQUIRED,
 
252
                                                   NULL, 0,             /* no creds, use ccache instead */
 
253
                                                   info.pg_krb5_ccache, &err_ret, NULL, NULL);
 
254
        if (retval)
 
255
        {
 
256
                if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
 
257
                {
 
258
#if defined(HAVE_KRB5_ERROR_TEXT_DATA)
 
259
                        printfPQExpBuffer(&conn->errorMessage,
 
260
                                  libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
 
261
                                                          (int) err_ret->text.length, err_ret->text.data);
 
262
#elif defined(HAVE_KRB5_ERROR_E_DATA)
 
263
                        printfPQExpBuffer(&conn->errorMessage,
 
264
                                  libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
 
265
                                                          (int) err_ret->e_data->length,
 
266
                                                          (const char *) err_ret->e_data->data);
 
267
#else
 
268
#error "bogus configuration"
 
269
#endif
 
270
                }
 
271
                else
 
272
                {
 
273
                        printfPQExpBuffer(&conn->errorMessage,
 
274
                                                          "krb5_sendauth: %s\n", error_message(retval));
 
275
                }
 
276
 
 
277
                if (err_ret)
 
278
                        krb5_free_error(info.pg_krb5_context, err_ret);
 
279
 
 
280
                ret = STATUS_ERROR;
 
281
        }
 
282
 
 
283
        krb5_free_principal(info.pg_krb5_context, server);
 
284
 
 
285
        if (!pg_set_noblock(conn->sock))
 
286
        {
 
287
                char            sebuf[256];
 
288
 
 
289
                printfPQExpBuffer(&conn->errorMessage,
 
290
                libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
 
291
                                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
 
292
                ret = STATUS_ERROR;
 
293
        }
 
294
        pg_krb5_destroy(&info);
 
295
 
 
296
        return ret;
 
297
}
 
298
#endif   /* KRB5 */
 
299
 
 
300
#ifdef ENABLE_GSS
 
301
/*
 
302
 * GSSAPI authentication system.
 
303
 */
 
304
 
 
305
#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
 
306
/*
 
307
 * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
 
308
 * that contain the OIDs required. Redefine here, values copied
 
309
 * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
 
310
 */
 
311
static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
 
312
{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
 
313
static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
 
314
#endif
 
315
 
 
316
/*
 
317
 * Fetch all errors of a specific type and append to "str".
 
318
 */
 
319
static void
 
320
pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
 
321
                                 OM_uint32 stat, int type)
 
322
{
 
323
        OM_uint32       lmaj_s,
 
324
                                lmin_s;
 
325
        gss_buffer_desc lmsg;
 
326
        OM_uint32       msg_ctx = 0;
 
327
 
 
328
        do
 
329
        {
 
330
                lmaj_s = gss_display_status(&lmin_s, stat, type,
 
331
                                                                        GSS_C_NO_OID, &msg_ctx, &lmsg);
 
332
                appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
 
333
                gss_release_buffer(&lmin_s, &lmsg);
 
334
        } while (msg_ctx);
 
335
}
 
336
 
 
337
/*
 
338
 * GSSAPI errors contain two parts; put both into conn->errorMessage.
 
339
 */
 
340
static void
 
341
pg_GSS_error(const char *mprefix, PGconn *conn,
 
342
                         OM_uint32 maj_stat, OM_uint32 min_stat)
 
343
{
 
344
        resetPQExpBuffer(&conn->errorMessage);
 
345
 
 
346
        /* Fetch major error codes */
 
347
        pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
 
348
 
 
349
        /* Add the minor codes as well */
 
350
        pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
 
351
}
 
352
 
 
353
/*
 
354
 * Continue GSS authentication with next token as needed.
 
355
 */
 
356
static int
 
357
pg_GSS_continue(PGconn *conn)
 
358
{
 
359
        OM_uint32       maj_stat,
 
360
                                min_stat,
 
361
                                lmin_s;
 
362
 
 
363
        maj_stat = gss_init_sec_context(&min_stat,
 
364
                                                                        GSS_C_NO_CREDENTIAL,
 
365
                                                                        &conn->gctx,
 
366
                                                                        conn->gtarg_nam,
 
367
                                                                        GSS_C_NO_OID,
 
368
                                                                        GSS_C_MUTUAL_FLAG,
 
369
                                                                        0,
 
370
                                                                        GSS_C_NO_CHANNEL_BINDINGS,
 
371
                  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
 
372
                                                                        NULL,
 
373
                                                                        &conn->goutbuf,
 
374
                                                                        NULL,
 
375
                                                                        NULL);
 
376
 
 
377
        if (conn->gctx != GSS_C_NO_CONTEXT)
 
378
        {
 
379
                free(conn->ginbuf.value);
 
380
                conn->ginbuf.value = NULL;
 
381
                conn->ginbuf.length = 0;
 
382
        }
 
383
 
 
384
        if (conn->goutbuf.length != 0)
 
385
        {
 
386
                /*
 
387
                 * GSS generated data to send to the server. We don't care if it's the
 
388
                 * first or subsequent packet, just send the same kind of password
 
389
                 * packet.
 
390
                 */
 
391
                if (pqPacketSend(conn, 'p',
 
392
                                                 conn->goutbuf.value, conn->goutbuf.length)
 
393
                        != STATUS_OK)
 
394
                {
 
395
                        gss_release_buffer(&lmin_s, &conn->goutbuf);
 
396
                        return STATUS_ERROR;
 
397
                }
 
398
        }
 
399
        gss_release_buffer(&lmin_s, &conn->goutbuf);
 
400
 
 
401
        if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
 
402
        {
 
403
                pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
 
404
                                         conn,
 
405
                                         maj_stat, min_stat);
 
406
                gss_release_name(&lmin_s, &conn->gtarg_nam);
 
407
                if (conn->gctx)
 
408
                        gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
 
409
                return STATUS_ERROR;
 
410
        }
 
411
 
 
412
        if (maj_stat == GSS_S_COMPLETE)
 
413
                gss_release_name(&lmin_s, &conn->gtarg_nam);
 
414
 
 
415
        return STATUS_OK;
 
416
}
 
417
 
 
418
/*
 
419
 * Send initial GSS authentication token
 
420
 */
 
421
static int
 
422
pg_GSS_startup(PGconn *conn)
 
423
{
 
424
        OM_uint32       maj_stat,
 
425
                                min_stat;
 
426
        int                     maxlen;
 
427
        gss_buffer_desc temp_gbuf;
 
428
 
 
429
        if (conn->gctx)
 
430
        {
 
431
                printfPQExpBuffer(&conn->errorMessage,
 
432
                                        libpq_gettext("duplicate GSS authentication request\n"));
 
433
                return STATUS_ERROR;
 
434
        }
 
435
 
 
436
        /*
 
437
         * Import service principal name so the proper ticket can be acquired by
 
438
         * the GSSAPI system.
 
439
         */
 
440
        maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
 
441
        temp_gbuf.value = (char *) malloc(maxlen);
 
442
        snprintf(temp_gbuf.value, maxlen, "%s@%s",
 
443
                         conn->krbsrvname, conn->pghost);
 
444
        temp_gbuf.length = strlen(temp_gbuf.value);
 
445
 
 
446
        maj_stat = gss_import_name(&min_stat, &temp_gbuf,
 
447
                                                           GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
 
448
        free(temp_gbuf.value);
 
449
 
 
450
        if (maj_stat != GSS_S_COMPLETE)
 
451
        {
 
452
                pg_GSS_error(libpq_gettext("GSSAPI name import error"),
 
453
                                         conn,
 
454
                                         maj_stat, min_stat);
 
455
                return STATUS_ERROR;
 
456
        }
 
457
 
 
458
        /*
 
459
         * Initial packet is the same as a continuation packet with no initial
 
460
         * context.
 
461
         */
 
462
        conn->gctx = GSS_C_NO_CONTEXT;
 
463
 
 
464
        return pg_GSS_continue(conn);
 
465
}
 
466
#endif   /* ENABLE_GSS */
 
467
 
 
468
 
 
469
#ifdef ENABLE_SSPI
 
470
/*
 
471
 * SSPI authentication system (Windows only)
 
472
 */
 
473
 
 
474
static void
 
475
pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r)
 
476
{
 
477
        char            sysmsg[256];
 
478
 
 
479
        if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0,
 
480
                                          sysmsg, sizeof(sysmsg), NULL) == 0)
 
481
                printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x",
 
482
                                                  mprefix, (unsigned int) r);
 
483
        else
 
484
                printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)",
 
485
                                                  mprefix, sysmsg, (unsigned int) r);
 
486
}
 
487
 
 
488
/*
 
489
 * Continue SSPI authentication with next token as needed.
 
490
 */
 
491
static int
 
492
pg_SSPI_continue(PGconn *conn)
 
493
{
 
494
        SECURITY_STATUS r;
 
495
        CtxtHandle      newContext;
 
496
        ULONG           contextAttr;
 
497
        SecBufferDesc inbuf;
 
498
        SecBufferDesc outbuf;
 
499
        SecBuffer       OutBuffers[1];
 
500
        SecBuffer       InBuffers[1];
 
501
 
 
502
        if (conn->sspictx != NULL)
 
503
        {
 
504
                /*
 
505
                 * On runs other than the first we have some data to send. Put this
 
506
                 * data in a SecBuffer type structure.
 
507
                 */
 
508
                inbuf.ulVersion = SECBUFFER_VERSION;
 
509
                inbuf.cBuffers = 1;
 
510
                inbuf.pBuffers = InBuffers;
 
511
                InBuffers[0].pvBuffer = conn->ginbuf.value;
 
512
                InBuffers[0].cbBuffer = conn->ginbuf.length;
 
513
                InBuffers[0].BufferType = SECBUFFER_TOKEN;
 
514
        }
 
515
 
 
516
        OutBuffers[0].pvBuffer = NULL;
 
517
        OutBuffers[0].BufferType = SECBUFFER_TOKEN;
 
518
        OutBuffers[0].cbBuffer = 0;
 
519
        outbuf.cBuffers = 1;
 
520
        outbuf.pBuffers = OutBuffers;
 
521
        outbuf.ulVersion = SECBUFFER_VERSION;
 
522
 
 
523
        r = InitializeSecurityContext(conn->sspicred,
 
524
                                                                  conn->sspictx,
 
525
                                                                  conn->sspitarget,
 
526
                                                                  ISC_REQ_ALLOCATE_MEMORY,
 
527
                                                                  0,
 
528
                                                                  SECURITY_NETWORK_DREP,
 
529
                                                                  (conn->sspictx == NULL) ? NULL : &inbuf,
 
530
                                                                  0,
 
531
                                                                  &newContext,
 
532
                                                                  &outbuf,
 
533
                                                                  &contextAttr,
 
534
                                                                  NULL);
 
535
 
 
536
        if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
 
537
        {
 
538
                pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r);
 
539
 
 
540
                return STATUS_ERROR;
 
541
        }
 
542
 
 
543
        if (conn->sspictx == NULL)
 
544
        {
 
545
                /* On first run, transfer retreived context handle */
 
546
                conn->sspictx = malloc(sizeof(CtxtHandle));
 
547
                if (conn->sspictx == NULL)
 
548
                {
 
549
                        printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
 
550
                        return STATUS_ERROR;
 
551
                }
 
552
                memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
 
553
        }
 
554
        else
 
555
        {
 
556
                /*
 
557
                 * On subsequent runs when we had data to send, free buffers that
 
558
                 * contained this data.
 
559
                 */
 
560
                free(conn->ginbuf.value);
 
561
                conn->ginbuf.value = NULL;
 
562
                conn->ginbuf.length = 0;
 
563
        }
 
564
 
 
565
        /*
 
566
         * If SSPI returned any data to be sent to the server (as it normally
 
567
         * would), send this data as a password packet.
 
568
         */
 
569
        if (outbuf.cBuffers > 0)
 
570
        {
 
571
                if (outbuf.cBuffers != 1)
 
572
                {
 
573
                        /*
 
574
                         * This should never happen, at least not for Kerberos
 
575
                         * authentication. Keep check in case it shows up with other
 
576
                         * authentication methods later.
 
577
                         */
 
578
                        printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
 
579
                        return STATUS_ERROR;
 
580
                }
 
581
 
 
582
                /*
 
583
                 * If the negotiation is complete, there may be zero bytes to send. The server is
 
584
                 * at this point not expecting any more data, so don't send it.
 
585
                 */
 
586
                if (outbuf.pBuffers[0].cbBuffer > 0)
 
587
                {
 
588
                        if (pqPacketSend(conn, 'p',
 
589
                                           outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer))
 
590
                        {
 
591
                                FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
 
592
                                return STATUS_ERROR;
 
593
                        }
 
594
                }
 
595
                FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
 
596
        }
 
597
 
 
598
        /* Cleanup is handled by the code in freePGconn() */
 
599
        return STATUS_OK;
 
600
}
 
601
 
 
602
/*
 
603
 * Send initial SSPI authentication token.
 
604
 * If use_negotiate is 0, use kerberos authentication package which is
 
605
 * compatible with Unix. If use_negotiate is 1, use the negotiate package
 
606
 * which supports both kerberos and NTLM, but is not compatible with Unix.
 
607
 */
 
608
static int
 
609
pg_SSPI_startup(PGconn *conn, int use_negotiate)
 
610
{
 
611
        SECURITY_STATUS r;
 
612
        TimeStamp       expire;
 
613
 
 
614
        conn->sspictx = NULL;
 
615
 
 
616
        /*
 
617
         * Retreive credentials handle
 
618
         */
 
619
        conn->sspicred = malloc(sizeof(CredHandle));
 
620
        if (conn->sspicred == NULL)
 
621
        {
 
622
                printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
 
623
                return STATUS_ERROR;
 
624
        }
 
625
 
 
626
        r = AcquireCredentialsHandle(NULL,
 
627
                                                                 use_negotiate ? "negotiate" : "kerberos",
 
628
                                                                 SECPKG_CRED_OUTBOUND,
 
629
                                                                 NULL,
 
630
                                                                 NULL,
 
631
                                                                 NULL,
 
632
                                                                 NULL,
 
633
                                                                 conn->sspicred,
 
634
                                                                 &expire);
 
635
        if (r != SEC_E_OK)
 
636
        {
 
637
                pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r);
 
638
                free(conn->sspicred);
 
639
                conn->sspicred = NULL;
 
640
                return STATUS_ERROR;
 
641
        }
 
642
 
 
643
        /*
 
644
         * Compute target principal name. SSPI has a different format from GSSAPI,
 
645
         * but not more complex. We can skip the @REALM part, because Windows will
 
646
         * fill that in for us automatically.
 
647
         */
 
648
        if (conn->pghost == NULL)
 
649
        {
 
650
                printfPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n"));
 
651
                return STATUS_ERROR;
 
652
        }
 
653
        conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2);
 
654
        if (!conn->sspitarget)
 
655
        {
 
656
                printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
 
657
                return STATUS_ERROR;
 
658
        }
 
659
        sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost);
 
660
 
 
661
        /*
 
662
         * Indicate that we're in SSPI authentication mode to make sure that
 
663
         * pg_SSPI_continue is called next time in the negotiation.
 
664
         */
 
665
        conn->usesspi = 1;
 
666
 
 
667
        return pg_SSPI_continue(conn);
 
668
}
 
669
#endif   /* ENABLE_SSPI */
 
670
 
 
671
/*
 
672
 * Respond to AUTH_REQ_SCM_CREDS challenge.
 
673
 *
 
674
 * Note: current backends will not use this challenge if HAVE_GETPEEREID
 
675
 * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the
 
676
 * code anyway.
 
677
 */
 
678
static int
 
679
pg_local_sendauth(PGconn *conn)
 
680
{
 
681
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
 
682
        (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
 
683
        char            buf;
 
684
        struct iovec iov;
 
685
        struct msghdr msg;
 
686
 
 
687
#ifdef HAVE_STRUCT_CMSGCRED
 
688
        /* Prevent padding */
 
689
        char            cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
 
690
 
 
691
        /* Point to start of first structure */
 
692
        struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
 
693
#endif
 
694
 
 
695
        /*
 
696
         * The backend doesn't care what we send here, but it wants exactly one
 
697
         * character to force recvmsg() to block and wait for us.
 
698
         */
 
699
        buf = '\0';
 
700
        iov.iov_base = &buf;
 
701
        iov.iov_len = 1;
 
702
 
 
703
        memset(&msg, 0, sizeof(msg));
 
704
        msg.msg_iov = &iov;
 
705
        msg.msg_iovlen = 1;
 
706
 
 
707
#ifdef HAVE_STRUCT_CMSGCRED
 
708
        /* Create control header, FreeBSD */
 
709
        msg.msg_control = cmsg;
 
710
        msg.msg_controllen = sizeof(cmsgmem);
 
711
        memset(cmsg, 0, sizeof(cmsgmem));
 
712
        cmsg->cmsg_len = sizeof(cmsgmem);
 
713
        cmsg->cmsg_level = SOL_SOCKET;
 
714
        cmsg->cmsg_type = SCM_CREDS;
 
715
#endif
 
716
 
 
717
        if (sendmsg(conn->sock, &msg, 0) == -1)
 
718
        {
 
719
                char            sebuf[256];
 
720
 
 
721
                printfPQExpBuffer(&conn->errorMessage,
 
722
                                                  "pg_local_sendauth: sendmsg: %s\n",
 
723
                                                  pqStrerror(errno, sebuf, sizeof(sebuf)));
 
724
                return STATUS_ERROR;
 
725
        }
 
726
        return STATUS_OK;
 
727
#else
 
728
        printfPQExpBuffer(&conn->errorMessage,
 
729
                        libpq_gettext("SCM_CRED authentication method not supported\n"));
 
730
        return STATUS_ERROR;
 
731
#endif
 
732
}
 
733
 
 
734
static int
 
735
pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 
736
{
 
737
        int                     ret;
 
738
        char       *crypt_pwd;
 
739
 
 
740
        /* Encrypt the password if needed. */
 
741
 
 
742
        switch (areq)
 
743
        {
 
744
                case AUTH_REQ_MD5:
 
745
                        {
 
746
                                char       *crypt_pwd2;
 
747
 
 
748
                                /* Allocate enough space for two MD5 hashes */
 
749
                                crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
 
750
                                if (!crypt_pwd)
 
751
                                {
 
752
                                        printfPQExpBuffer(&conn->errorMessage,
 
753
                                                                          libpq_gettext("out of memory\n"));
 
754
                                        return STATUS_ERROR;
 
755
                                }
 
756
 
 
757
                                crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
 
758
                                if (!pg_md5_encrypt(password, conn->pguser,
 
759
                                                                        strlen(conn->pguser), crypt_pwd2))
 
760
                                {
 
761
                                        free(crypt_pwd);
 
762
                                        return STATUS_ERROR;
 
763
                                }
 
764
                                if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
 
765
                                                                        sizeof(conn->md5Salt), crypt_pwd))
 
766
                                {
 
767
                                        free(crypt_pwd);
 
768
                                        return STATUS_ERROR;
 
769
                                }
 
770
                                break;
 
771
                        }
 
772
                case AUTH_REQ_PASSWORD:
 
773
                        /* discard const so we can assign it */
 
774
                        crypt_pwd = (char *) password;
 
775
                        break;
 
776
                default:
 
777
                        return STATUS_ERROR;
 
778
        }
 
779
        /* Packet has a message type as of protocol 3.0 */
 
780
        if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
 
781
                ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
 
782
        else
 
783
                ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
 
784
        if (areq == AUTH_REQ_MD5)
 
785
                free(crypt_pwd);
 
786
        return ret;
 
787
}
 
788
 
 
789
/*
 
790
 * pg_fe_sendauth
 
791
 *              client demux routine for outgoing authentication information
 
792
 */
 
793
int
 
794
pg_fe_sendauth(AuthRequest areq, PGconn *conn)
 
795
{
 
796
        switch (areq)
 
797
        {
 
798
                case AUTH_REQ_OK:
 
799
                        break;
 
800
 
 
801
                case AUTH_REQ_KRB4:
 
802
                        printfPQExpBuffer(&conn->errorMessage,
 
803
                                 libpq_gettext("Kerberos 4 authentication not supported\n"));
 
804
                        return STATUS_ERROR;
 
805
 
 
806
                case AUTH_REQ_KRB5:
 
807
#ifdef KRB5
 
808
                        pglock_thread();
 
809
                        if (pg_krb5_sendauth(conn) != STATUS_OK)
 
810
                        {
 
811
                                /* Error message already filled in */
 
812
                                pgunlock_thread();
 
813
                                return STATUS_ERROR;
 
814
                        }
 
815
                        pgunlock_thread();
 
816
                        break;
 
817
#else
 
818
                        printfPQExpBuffer(&conn->errorMessage,
 
819
                                 libpq_gettext("Kerberos 5 authentication not supported\n"));
 
820
                        return STATUS_ERROR;
 
821
#endif
 
822
 
 
823
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 
824
                case AUTH_REQ_GSS:
 
825
                        {
 
826
                                int                     r;
 
827
 
 
828
                                pglock_thread();
 
829
 
 
830
                                /*
 
831
                                 * If we have both GSS and SSPI support compiled in, use SSPI
 
832
                                 * support by default. This is overridable by a connection
 
833
                                 * string parameter. Note that when using SSPI we still leave
 
834
                                 * the negotiate parameter off, since we want SSPI to use the
 
835
                                 * GSSAPI kerberos protocol. For actual SSPI negotiate
 
836
                                 * protocol, we use AUTH_REQ_SSPI.
 
837
                                 */
 
838
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 
839
                                if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0))
 
840
                                        r = pg_GSS_startup(conn);
 
841
                                else
 
842
                                        r = pg_SSPI_startup(conn, 0);
 
843
#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
 
844
                                r = pg_GSS_startup(conn);
 
845
#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 
846
                                r = pg_SSPI_startup(conn, 0);
 
847
#endif
 
848
                                if (r != STATUS_OK)
 
849
                                {
 
850
                                        /* Error message already filled in. */
 
851
                                        pgunlock_thread();
 
852
                                        return STATUS_ERROR;
 
853
                                }
 
854
                                pgunlock_thread();
 
855
                        }
 
856
                        break;
 
857
 
 
858
                case AUTH_REQ_GSS_CONT:
 
859
                        {
 
860
                                int                     r;
 
861
 
 
862
                                pglock_thread();
 
863
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 
864
                                if (conn->usesspi)
 
865
                                        r = pg_SSPI_continue(conn);
 
866
                                else
 
867
                                        r = pg_GSS_continue(conn);
 
868
#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
 
869
                                r = pg_GSS_continue(conn);
 
870
#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 
871
                                r = pg_SSPI_continue(conn);
 
872
#endif
 
873
                                if (r != STATUS_OK)
 
874
                                {
 
875
                                        /* Error message already filled in. */
 
876
                                        pgunlock_thread();
 
877
                                        return STATUS_ERROR;
 
878
                                }
 
879
                                pgunlock_thread();
 
880
                        }
 
881
                        break;
 
882
#else
 
883
                case AUTH_REQ_GSS:
 
884
                case AUTH_REQ_GSS_CONT:
 
885
                        printfPQExpBuffer(&conn->errorMessage,
 
886
                                         libpq_gettext("GSSAPI authentication not supported\n"));
 
887
                        return STATUS_ERROR;
 
888
#endif
 
889
 
 
890
#ifdef ENABLE_SSPI
 
891
                case AUTH_REQ_SSPI:
 
892
 
 
893
                        /*
 
894
                         * SSPI has it's own startup message so libpq can decide which
 
895
                         * method to use. Indicate to pg_SSPI_startup that we want SSPI
 
896
                         * negotiation instead of Kerberos.
 
897
                         */
 
898
                        pglock_thread();
 
899
                        if (pg_SSPI_startup(conn, 1) != STATUS_OK)
 
900
                        {
 
901
                                /* Error message already filled in. */
 
902
                                pgunlock_thread();
 
903
                                return STATUS_ERROR;
 
904
                        }
 
905
                        pgunlock_thread();
 
906
                        break;
 
907
#else
 
908
                case AUTH_REQ_SSPI:
 
909
                        printfPQExpBuffer(&conn->errorMessage,
 
910
                                           libpq_gettext("SSPI authentication not supported\n"));
 
911
                        return STATUS_ERROR;
 
912
#endif
 
913
 
 
914
 
 
915
                case AUTH_REQ_CRYPT:
 
916
                        printfPQExpBuffer(&conn->errorMessage,
 
917
                                 libpq_gettext("Crypt authentication not supported\n"));
 
918
                        return STATUS_ERROR;
 
919
 
 
920
                case AUTH_REQ_MD5:
 
921
                case AUTH_REQ_PASSWORD:
 
922
                        conn->password_needed = true;
 
923
                        if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
 
924
                        {
 
925
                                printfPQExpBuffer(&conn->errorMessage,
 
926
                                                                  PQnoPasswordSupplied);
 
927
                                return STATUS_ERROR;
 
928
                        }
 
929
                        if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK)
 
930
                        {
 
931
                                printfPQExpBuffer(&conn->errorMessage,
 
932
                                         "fe_sendauth: error sending password authentication\n");
 
933
                                return STATUS_ERROR;
 
934
                        }
 
935
                        break;
 
936
 
 
937
                case AUTH_REQ_SCM_CREDS:
 
938
                        if (pg_local_sendauth(conn) != STATUS_OK)
 
939
                                return STATUS_ERROR;
 
940
                        break;
 
941
 
 
942
                default:
 
943
                        printfPQExpBuffer(&conn->errorMessage,
 
944
                        libpq_gettext("authentication method %u not supported\n"), areq);
 
945
                        return STATUS_ERROR;
 
946
        }
 
947
 
 
948
        return STATUS_OK;
 
949
}
 
950
 
 
951
 
 
952
/*
 
953
 * pg_fe_getauthname -- returns a pointer to dynamic space containing whatever
 
954
 *                                       name the user has authenticated to the system
 
955
 *
 
956
 * if there is an error, return NULL with an error message in errorMessage
 
957
 */
 
958
char *
 
959
pg_fe_getauthname(PQExpBuffer errorMessage)
 
960
{
 
961
        const char *name = NULL;
 
962
        char       *authn;
 
963
 
 
964
#ifdef WIN32
 
965
        char            username[128];
 
966
        DWORD           namesize = sizeof(username) - 1;
 
967
#else
 
968
        char            pwdbuf[BUFSIZ];
 
969
        struct passwd pwdstr;
 
970
        struct passwd *pw = NULL;
 
971
#endif
 
972
 
 
973
        /*
 
974
         * Some users are using configure
 
975
         * --enable-thread-safety-force, so we might as well do the locking within
 
976
         * our library to protect pqGetpwuid(). In fact, application developers
 
977
         * can use getpwuid() in their application if they use the locking call we
 
978
         * provide, or install their own locking function using
 
979
         * PQregisterThreadLock().
 
980
         */
 
981
        pglock_thread();
 
982
 
 
983
        if (!name)
 
984
        {
 
985
#ifdef WIN32
 
986
                if (GetUserName(username, &namesize))
 
987
                        name = username;
 
988
#else
 
989
                if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
 
990
                        name = pw->pw_name;
 
991
#endif
 
992
        }
 
993
 
 
994
        authn = name ? strdup(name) : NULL;
 
995
 
 
996
        pgunlock_thread();
 
997
 
 
998
        return authn;
 
999
}
 
1000
 
 
1001
 
 
1002
/*
 
1003
 * PQencryptPassword -- exported routine to encrypt a password
 
1004
 *
 
1005
 * This is intended to be used by client applications that wish to send
 
1006
 * commands like ALTER USER joe PASSWORD 'pwd'.  The password need not
 
1007
 * be sent in cleartext if it is encrypted on the client side.  This is
 
1008
 * good because it ensures the cleartext password won't end up in logs,
 
1009
 * pg_stat displays, etc.  We export the function so that clients won't
 
1010
 * be dependent on low-level details like whether the enceyption is MD5
 
1011
 * or something else.
 
1012
 *
 
1013
 * Arguments are the cleartext password, and the SQL name of the user it
 
1014
 * is for.
 
1015
 *
 
1016
 * Return value is a malloc'd string, or NULL if out-of-memory.  The client
 
1017
 * may assume the string doesn't contain any special characters that would
 
1018
 * require escaping.
 
1019
 */
 
1020
char *
 
1021
PQencryptPassword(const char *passwd, const char *user)
 
1022
{
 
1023
        char       *crypt_pwd;
 
1024
 
 
1025
        crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
 
1026
        if (!crypt_pwd)
 
1027
                return NULL;
 
1028
 
 
1029
        if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
 
1030
        {
 
1031
                free(crypt_pwd);
 
1032
                return NULL;
 
1033
        }
 
1034
 
 
1035
        return crypt_pwd;
 
1036
}