~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

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

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

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-2005, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 * NOTE: the error message strings returned by this module must not
 
10
 * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
 
11
 *
 
12
 * IDENTIFICATION
 
13
 *        $PostgreSQL: pgsql/src/interfaces/libpq/fe-auth.c,v 1.99.4.1 2005-03-25 00:35:19 tgl Exp $
 
14
 *
 
15
 *-------------------------------------------------------------------------
 
16
 */
 
17
 
 
18
/*
 
19
 * INTERFACE ROUTINES
 
20
 *         frontend (client) routines:
 
21
 *              fe_sendauth                             send authentication information
 
22
 *              fe_getauthname                  get user's name according to the client side
 
23
 *                                                              of the authentication system
 
24
 *              fe_setauthsvc                   set frontend authentication service
 
25
 *              fe_getauthsvc                   get current frontend authentication service
 
26
 *
 
27
 *
 
28
 *
 
29
 */
 
30
 
 
31
#include "postgres_fe.h"
 
32
 
 
33
#ifdef WIN32
 
34
#include "win32.h"
 
35
#else
 
36
#include <unistd.h>
 
37
#include <fcntl.h>
 
38
#include <errno.h>
 
39
#include <sys/types.h>
 
40
#include <sys/param.h>                  /* for MAXHOSTNAMELEN on most */
 
41
#include <sys/socket.h>
 
42
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
 
43
#include <sys/uio.h>
 
44
#include <sys/ucred.h>
 
45
#endif
 
46
#ifndef  MAXHOSTNAMELEN
 
47
#include <netdb.h>                              /* for MAXHOSTNAMELEN on some */
 
48
#endif
 
49
#include <pwd.h>
 
50
#endif
 
51
 
 
52
#ifdef HAVE_CRYPT_H
 
53
#include <crypt.h>
 
54
#endif
 
55
 
 
56
#include "libpq-fe.h"
 
57
#include "libpq-int.h"
 
58
#include "fe-auth.h"
 
59
#include "libpq/crypt.h"
 
60
 
 
61
 
 
62
/*
 
63
 * common definitions for generic fe/be routines
 
64
 */
 
65
 
 
66
#define STARTUP_MSG             7               /* Initialise a connection */
 
67
#define STARTUP_KRB4_MSG        10      /* krb4 session follows */
 
68
#define STARTUP_KRB5_MSG        11      /* krb5 session follows */
 
69
#define STARTUP_PASSWORD_MSG    14              /* Password follows */
 
70
 
 
71
struct authsvc
 
72
{
 
73
        const char *name;                       /* service nickname (for command line) */
 
74
        MsgType         msgtype;                /* startup packet header type */
 
75
        int                     allowed;                /* initially allowed (before command line
 
76
                                                                 * option parsing)? */
 
77
};
 
78
 
 
79
/*
 
80
 * Command-line parsing routines use this structure to map nicknames
 
81
 * onto service types (and the startup packets to use with them).
 
82
 *
 
83
 * Programs receiving an authentication request use this structure to
 
84
 * decide which authentication service types are currently permitted.
 
85
 * By default, all authentication systems compiled into the system are
 
86
 * allowed.  Unauthenticated connections are disallowed unless there
 
87
 * isn't any authentication system.
 
88
 */
 
89
static const struct authsvc authsvcs[] = {
 
90
#ifdef KRB4
 
91
        {"krb4", STARTUP_KRB4_MSG, 1},
 
92
        {"kerberos", STARTUP_KRB4_MSG, 1},
 
93
#endif   /* KRB4 */
 
94
#ifdef KRB5
 
95
        {"krb5", STARTUP_KRB5_MSG, 1},
 
96
        {"kerberos", STARTUP_KRB5_MSG, 1},
 
97
#endif   /* KRB5 */
 
98
        {UNAUTHNAME, STARTUP_MSG,
 
99
#if defined(KRB4) || defined(KRB5)
 
100
                0
 
101
#else                                                   /* !(KRB4 || KRB5) */
 
102
                1
 
103
#endif   /* !(KRB4 || KRB5) */
 
104
        },
 
105
        {"password", STARTUP_PASSWORD_MSG, 0}
 
106
};
 
107
 
 
108
static const int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
 
109
 
 
110
#ifdef KRB4
 
111
/*
 
112
 * MIT Kerberos authentication system - protocol version 4
 
113
 */
 
114
 
 
115
#include "krb.h"
 
116
 
 
117
/* for some reason, this is not defined in krb.h ... */
 
118
extern char *tkt_string(void);
 
119
 
 
120
/*
 
121
 * pg_krb4_init -- initialization performed before any Kerberos calls are made
 
122
 *
 
123
 * For v4, all we need to do is make sure the library routines get the right
 
124
 * ticket file if we want them to see a special one.  (They will open the file
 
125
 * themselves.)
 
126
 */
 
127
static void
 
128
pg_krb4_init()
 
129
{
 
130
        char       *realm;
 
131
        static int      init_done = 0;
 
132
 
 
133
        if (init_done)
 
134
                return;
 
135
        init_done = 1;
 
136
 
 
137
        /*
 
138
         * If the user set PGREALM, then we use a ticket file with a special
 
139
         * name: <usual-ticket-file-name>@<PGREALM-value>
 
140
         */
 
141
        if ((realm = getenv("PGREALM")))
 
142
        {
 
143
                char            tktbuf[MAXPGPATH];
 
144
 
 
145
                (void) snprintf(tktbuf, sizeof(tktbuf), "%s@%s", tkt_string(), realm);
 
146
                krb_set_tkt_string(tktbuf);
 
147
        }
 
148
}
 
149
 
 
150
/*
 
151
 * pg_krb4_authname -- returns a pointer to static space containing whatever
 
152
 *                                         name the user has authenticated to the system
 
153
 *
 
154
 * We obtain this information by digging around in the ticket file.
 
155
 */
 
156
static char *
 
157
pg_krb4_authname(char *PQerrormsg)
 
158
{
 
159
        char            instance[INST_SZ + 1];
 
160
        char            realm[REALM_SZ + 1];
 
161
        int                     status;
 
162
        static char name[SNAME_SZ + 1] = "";
 
163
 
 
164
        if (name[0])
 
165
                return name;
 
166
 
 
167
        pg_krb4_init();
 
168
 
 
169
        name[SNAME_SZ] = '\0';
 
170
        status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
 
171
        if (status != KSUCCESS)
 
172
        {
 
173
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
174
                                 "pg_krb4_authname: krb_get_tf_fullname: %s\n",
 
175
                                 krb_err_txt[status]);
 
176
                return NULL;
 
177
        }
 
178
        return name;
 
179
}
 
180
 
 
181
/*
 
182
 * pg_krb4_sendauth -- client routine to send authentication information to
 
183
 *                                         the server
 
184
 *
 
185
 * This routine does not do mutual authentication, nor does it return enough
 
186
 * information to do encrypted connections.  But then, if we want to do
 
187
 * encrypted connections, we'll have to redesign the whole RPC mechanism
 
188
 * anyway.
 
189
 *
 
190
 * If the user is too lazy to feed us a hostname, we try to come up with
 
191
 * something other than "localhost" since the hostname is used as an
 
192
 * instance and instance names in v4 databases are usually actual hostnames
 
193
 * (canonicalized to omit all domain suffixes).
 
194
 */
 
195
static int
 
196
pg_krb4_sendauth(char *PQerrormsg, int sock,
 
197
                                 struct sockaddr_in * laddr,
 
198
                                 struct sockaddr_in * raddr,
 
199
                                 const char *hostname)
 
200
{
 
201
        long            krbopts = 0;    /* one-way authentication */
 
202
        KTEXT_ST        clttkt;
 
203
        int                     status;
 
204
        char            hostbuf[MAXHOSTNAMELEN];
 
205
        const char *realm = getenv("PGREALM");          /* NULL == current realm */
 
206
 
 
207
        if (!hostname || !(*hostname))
 
208
        {
 
209
                if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
 
210
                        strcpy(hostbuf, "localhost");
 
211
                hostname = hostbuf;
 
212
        }
 
213
 
 
214
        pg_krb4_init();
 
215
 
 
216
        status = krb_sendauth(krbopts,
 
217
                                                  sock,
 
218
                                                  &clttkt,
 
219
                                                  PG_KRB_SRVNAM,
 
220
                                                  hostname,
 
221
                                                  realm,
 
222
                                                  (u_long) 0,
 
223
                                                  NULL,
 
224
                                                  NULL,
 
225
                                                  NULL,
 
226
                                                  laddr,
 
227
                                                  raddr,
 
228
                                                  PG_KRB4_VERSION);
 
229
        if (status != KSUCCESS)
 
230
        {
 
231
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
232
                                 libpq_gettext("Kerberos 4 error: %s\n"),
 
233
                                 krb_err_txt[status]);
 
234
                return STATUS_ERROR;
 
235
        }
 
236
        return STATUS_OK;
 
237
}
 
238
#endif   /* KRB4 */
 
239
 
 
240
#ifdef KRB5
 
241
/*
 
242
 * MIT Kerberos authentication system - protocol version 5
 
243
 */
 
244
 
 
245
#include <krb5.h>
 
246
/* Some old versions of Kerberos do not include <com_err.h> in <krb5.h> */
 
247
#if !defined(__COM_ERR_H) && !defined(__COM_ERR_H__)
 
248
#include <com_err.h>
 
249
#endif
 
250
 
 
251
/*
 
252
 * pg_an_to_ln -- return the local name corresponding to an authentication
 
253
 *                                name
 
254
 *
 
255
 * XXX Assumes that the first aname component is the user name.  This is NOT
 
256
 *         necessarily so, since an aname can actually be something out of your
 
257
 *         worst X.400 nightmare, like
 
258
 *                ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
 
259
 *         Note that the MIT an_to_ln code does the same thing if you don't
 
260
 *         provide an aname mapping database...it may be a better idea to use
 
261
 *         krb5_an_to_ln, except that it punts if multiple components are found,
 
262
 *         and we can't afford to punt.
 
263
 */
 
264
static char *
 
265
pg_an_to_ln(char *aname)
 
266
{
 
267
        char       *p;
 
268
 
 
269
        if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
 
270
                *p = '\0';
 
271
        return aname;
 
272
}
 
273
 
 
274
 
 
275
/*
 
276
 * Various krb5 state which is not connection specific, and a flag to
 
277
 * indicate whether we have initialised it yet.
 
278
 */
 
279
static int      pg_krb5_initialised;
 
280
static krb5_context pg_krb5_context;
 
281
static krb5_ccache pg_krb5_ccache;
 
282
static krb5_principal pg_krb5_client;
 
283
static char *pg_krb5_name;
 
284
 
 
285
 
 
286
static int
 
287
pg_krb5_init(char *PQerrormsg)
 
288
{
 
289
        krb5_error_code retval;
 
290
 
 
291
        if (pg_krb5_initialised)
 
292
                return STATUS_OK;
 
293
 
 
294
        retval = krb5_init_context(&pg_krb5_context);
 
295
        if (retval)
 
296
        {
 
297
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
298
                                 "pg_krb5_init: krb5_init_context: %s\n",
 
299
                                 error_message(retval));
 
300
                return STATUS_ERROR;
 
301
        }
 
302
 
 
303
        retval = krb5_cc_default(pg_krb5_context, &pg_krb5_ccache);
 
304
        if (retval)
 
305
        {
 
306
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
307
                                 "pg_krb5_init: krb5_cc_default: %s\n",
 
308
                                 error_message(retval));
 
309
                krb5_free_context(pg_krb5_context);
 
310
                return STATUS_ERROR;
 
311
        }
 
312
 
 
313
        retval = krb5_cc_get_principal(pg_krb5_context, pg_krb5_ccache,
 
314
                                                                   &pg_krb5_client);
 
315
        if (retval)
 
316
        {
 
317
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
318
                                 "pg_krb5_init: krb5_cc_get_principal: %s\n",
 
319
                                 error_message(retval));
 
320
                krb5_cc_close(pg_krb5_context, pg_krb5_ccache);
 
321
                krb5_free_context(pg_krb5_context);
 
322
                return STATUS_ERROR;
 
323
        }
 
324
 
 
325
        retval = krb5_unparse_name(pg_krb5_context, pg_krb5_client, &pg_krb5_name);
 
326
        if (retval)
 
327
        {
 
328
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
329
                                 "pg_krb5_init: krb5_unparse_name: %s\n",
 
330
                                 error_message(retval));
 
331
                krb5_free_principal(pg_krb5_context, pg_krb5_client);
 
332
                krb5_cc_close(pg_krb5_context, pg_krb5_ccache);
 
333
                krb5_free_context(pg_krb5_context);
 
334
                return STATUS_ERROR;
 
335
        }
 
336
 
 
337
        pg_krb5_name = pg_an_to_ln(pg_krb5_name);
 
338
 
 
339
        pg_krb5_initialised = 1;
 
340
        return STATUS_OK;
 
341
}
 
342
 
 
343
 
 
344
/*
 
345
 * pg_krb5_authname -- returns a pointer to static space containing whatever
 
346
 *                                         name the user has authenticated to the system
 
347
  */
 
348
static const char *
 
349
pg_krb5_authname(char *PQerrormsg)
 
350
{
 
351
        if (pg_krb5_init(PQerrormsg) != STATUS_OK)
 
352
                return NULL;
 
353
 
 
354
        return pg_krb5_name;
 
355
}
 
356
 
 
357
 
 
358
/*
 
359
 * pg_krb5_sendauth -- client routine to send authentication information to
 
360
 *                                         the server
 
361
 */
 
362
static int
 
363
pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname)
 
364
{
 
365
        krb5_error_code retval;
 
366
        int                     ret;
 
367
        krb5_principal server;
 
368
        krb5_auth_context auth_context = NULL;
 
369
        krb5_error *err_ret = NULL;
 
370
 
 
371
        if (!hostname)
 
372
        {
 
373
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
374
                                 "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n");
 
375
                return STATUS_ERROR;
 
376
        }
 
377
 
 
378
        ret = pg_krb5_init(PQerrormsg);
 
379
        if (ret != STATUS_OK)
 
380
                return ret;
 
381
 
 
382
        retval = krb5_sname_to_principal(pg_krb5_context, hostname, PG_KRB_SRVNAM,
 
383
                                                                         KRB5_NT_SRV_HST, &server);
 
384
        if (retval)
 
385
        {
 
386
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
387
                                 "pg_krb5_sendauth: krb5_sname_to_principal: %s\n",
 
388
                                 error_message(retval));
 
389
                return STATUS_ERROR;
 
390
        }
 
391
 
 
392
        /*
 
393
         * libpq uses a non-blocking socket. But kerberos needs a blocking
 
394
         * socket, and we have to block somehow to do mutual authentication
 
395
         * anyway. So we temporarily make it blocking.
 
396
         */
 
397
        if (!pg_set_block(sock))
 
398
        {
 
399
                char            sebuf[256];
 
400
 
 
401
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
402
                                 libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf)));
 
403
                krb5_free_principal(pg_krb5_context, server);
 
404
                return STATUS_ERROR;
 
405
        }
 
406
 
 
407
        retval = krb5_sendauth(pg_krb5_context, &auth_context,
 
408
                                                   (krb5_pointer) & sock, PG_KRB_SRVNAM,
 
409
                                                   pg_krb5_client, server,
 
410
                                                   AP_OPTS_MUTUAL_REQUIRED,
 
411
                                                   NULL, 0,             /* no creds, use ccache instead */
 
412
                                                   pg_krb5_ccache, &err_ret, NULL, NULL);
 
413
        if (retval)
 
414
        {
 
415
                if (retval == KRB5_SENDAUTH_REJECTED && err_ret)
 
416
                {
 
417
#if defined(HAVE_KRB5_ERROR_TEXT_DATA)
 
418
                        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
419
                          libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
 
420
                                         (int) err_ret->text.length, err_ret->text.data);
 
421
#elif defined(HAVE_KRB5_ERROR_E_DATA)
 
422
                        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
423
                          libpq_gettext("Kerberos 5 authentication rejected: %*s\n"),
 
424
                                         (int) err_ret->e_data->length,
 
425
                                         (const char *) err_ret->e_data->data);
 
426
#else
 
427
#error "bogus configuration"
 
428
#endif
 
429
                }
 
430
                else
 
431
                {
 
432
                        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
433
                                         "krb5_sendauth: %s\n", error_message(retval));
 
434
                }
 
435
 
 
436
                if (err_ret)
 
437
                        krb5_free_error(pg_krb5_context, err_ret);
 
438
 
 
439
                ret = STATUS_ERROR;
 
440
        }
 
441
 
 
442
        krb5_free_principal(pg_krb5_context, server);
 
443
 
 
444
        if (!pg_set_noblock(sock))
 
445
        {
 
446
                char            sebuf[256];
 
447
 
 
448
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
449
                                 libpq_gettext("could not restore non-blocking mode on socket: %s\n"),
 
450
                                 pqStrerror(errno, sebuf, sizeof(sebuf)));
 
451
                ret = STATUS_ERROR;
 
452
        }
 
453
 
 
454
        return ret;
 
455
}
 
456
#endif   /* KRB5 */
 
457
 
 
458
/*
 
459
 * Respond to AUTH_REQ_SCM_CREDS challenge.
 
460
 *
 
461
 * Note: current backends will not use this challenge if HAVE_GETPEEREID
 
462
 * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the
 
463
 * code anyway.
 
464
 */
 
465
static int
 
466
pg_local_sendauth(char *PQerrormsg, PGconn *conn)
 
467
{
 
468
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \
 
469
        (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
 
470
        char            buf;
 
471
        struct iovec iov;
 
472
        struct msghdr msg;
 
473
 
 
474
#ifdef HAVE_STRUCT_CMSGCRED
 
475
        /* Prevent padding */
 
476
        char            cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)];
 
477
 
 
478
        /* Point to start of first structure */
 
479
        struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
 
480
#endif
 
481
 
 
482
        /*
 
483
         * The backend doesn't care what we send here, but it wants exactly
 
484
         * one character to force recvmsg() to block and wait for us.
 
485
         */
 
486
        buf = '\0';
 
487
        iov.iov_base = &buf;
 
488
        iov.iov_len = 1;
 
489
 
 
490
        memset(&msg, 0, sizeof(msg));
 
491
        msg.msg_iov = &iov;
 
492
        msg.msg_iovlen = 1;
 
493
 
 
494
#ifdef HAVE_STRUCT_CMSGCRED
 
495
        /* Create control header, FreeBSD */
 
496
        msg.msg_control = cmsg;
 
497
        msg.msg_controllen = sizeof(cmsgmem);
 
498
        memset(cmsg, 0, sizeof(cmsgmem));
 
499
        cmsg->cmsg_len = sizeof(cmsgmem);
 
500
        cmsg->cmsg_level = SOL_SOCKET;
 
501
        cmsg->cmsg_type = SCM_CREDS;
 
502
#endif
 
503
 
 
504
        if (sendmsg(conn->sock, &msg, 0) == -1)
 
505
        {
 
506
                char            sebuf[256];
 
507
 
 
508
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
509
                                 "pg_local_sendauth: sendmsg: %s\n",
 
510
                                 pqStrerror(errno, sebuf, sizeof(sebuf)));
 
511
                return STATUS_ERROR;
 
512
        }
 
513
        return STATUS_OK;
 
514
#else
 
515
        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
516
                libpq_gettext("SCM_CRED authentication method not supported\n"));
 
517
        return STATUS_ERROR;
 
518
#endif
 
519
}
 
520
 
 
521
static int
 
522
pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 
523
{
 
524
        int                     ret;
 
525
        char       *crypt_pwd;
 
526
 
 
527
        /* Encrypt the password if needed. */
 
528
 
 
529
        switch (areq)
 
530
        {
 
531
                case AUTH_REQ_MD5:
 
532
                        {
 
533
                                char       *crypt_pwd2;
 
534
 
 
535
                                if (!(crypt_pwd = malloc(MD5_PASSWD_LEN + 1)) ||
 
536
                                        !(crypt_pwd2 = malloc(MD5_PASSWD_LEN + 1)))
 
537
                                {
 
538
                                        fprintf(stderr, libpq_gettext("out of memory\n"));
 
539
                                        return STATUS_ERROR;
 
540
                                }
 
541
                                if (!EncryptMD5(password, conn->pguser,
 
542
                                                                strlen(conn->pguser), crypt_pwd2))
 
543
                                {
 
544
                                        free(crypt_pwd);
 
545
                                        free(crypt_pwd2);
 
546
                                        return STATUS_ERROR;
 
547
                                }
 
548
                                if (!EncryptMD5(crypt_pwd2 + strlen("md5"), conn->md5Salt,
 
549
                                                                sizeof(conn->md5Salt), crypt_pwd))
 
550
                                {
 
551
                                        free(crypt_pwd);
 
552
                                        free(crypt_pwd2);
 
553
                                        return STATUS_ERROR;
 
554
                                }
 
555
                                free(crypt_pwd2);
 
556
                                break;
 
557
                        }
 
558
                case AUTH_REQ_CRYPT:
 
559
                        {
 
560
                                char            salt[3];
 
561
 
 
562
                                StrNCpy(salt, conn->cryptSalt, 3);
 
563
                                crypt_pwd = crypt(password, salt);
 
564
                                break;
 
565
                        }
 
566
                case AUTH_REQ_PASSWORD:
 
567
                        /* discard const so we can assign it */
 
568
                        crypt_pwd = (char *) password;
 
569
                        break;
 
570
                default:
 
571
                        return STATUS_ERROR;
 
572
        }
 
573
        /* Packet has a message type as of protocol 3.0 */
 
574
        if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
 
575
                ret = pqPacketSend(conn, 'p', crypt_pwd, strlen(crypt_pwd) + 1);
 
576
        else
 
577
                ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
 
578
        if (areq == AUTH_REQ_MD5)
 
579
                free(crypt_pwd);
 
580
        return ret;
 
581
}
 
582
 
 
583
/*
 
584
 * fe_sendauth -- client demux routine for outgoing authentication information
 
585
 */
 
586
int
 
587
fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
 
588
                        const char *password, char *PQerrormsg)
 
589
{
 
590
#if !defined(KRB4) && !defined(KRB5)
 
591
        (void) hostname;                        /* not used */
 
592
#endif
 
593
 
 
594
        switch (areq)
 
595
        {
 
596
                case AUTH_REQ_OK:
 
597
                        break;
 
598
 
 
599
                case AUTH_REQ_KRB4:
 
600
#ifdef KRB4
 
601
                        pglock_thread();
 
602
                        if (pg_krb4_sendauth(PQerrormsg, conn->sock,
 
603
                                                           (struct sockaddr_in *) & conn->laddr.addr,
 
604
                                                           (struct sockaddr_in *) & conn->raddr.addr,
 
605
                                                                 hostname) != STATUS_OK)
 
606
                        {
 
607
                                /* PQerrormsg already filled in */
 
608
                                pgunlock_thread();
 
609
                                return STATUS_ERROR;
 
610
                        }
 
611
                        pgunlock_thread();
 
612
                        break;
 
613
#else
 
614
                        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
615
                         libpq_gettext("Kerberos 4 authentication not supported\n"));
 
616
                        return STATUS_ERROR;
 
617
#endif
 
618
 
 
619
                case AUTH_REQ_KRB5:
 
620
#ifdef KRB5
 
621
                        pglock_thread();
 
622
                        if (pg_krb5_sendauth(PQerrormsg, conn->sock,
 
623
                                                                 hostname) != STATUS_OK)
 
624
                        {
 
625
                                /* PQerrormsg already filled in */
 
626
                                pgunlock_thread();
 
627
                                return STATUS_ERROR;
 
628
                        }
 
629
                        pgunlock_thread();
 
630
                        break;
 
631
#else
 
632
                        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
633
                         libpq_gettext("Kerberos 5 authentication not supported\n"));
 
634
                        return STATUS_ERROR;
 
635
#endif
 
636
 
 
637
                case AUTH_REQ_MD5:
 
638
                case AUTH_REQ_CRYPT:
 
639
                case AUTH_REQ_PASSWORD:
 
640
                        if (password == NULL || *password == '\0')
 
641
                        {
 
642
                                (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
643
                                                                PQnoPasswordSupplied);
 
644
                                return STATUS_ERROR;
 
645
                        }
 
646
                        if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
 
647
                        {
 
648
                                (void) snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
649
                                 "fe_sendauth: error sending password authentication\n");
 
650
                                return STATUS_ERROR;
 
651
                        }
 
652
                        break;
 
653
 
 
654
                case AUTH_REQ_SCM_CREDS:
 
655
                        if (pg_local_sendauth(PQerrormsg, conn) != STATUS_OK)
 
656
                                return STATUS_ERROR;
 
657
                        break;
 
658
 
 
659
                default:
 
660
                        snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
661
                                         libpq_gettext("authentication method %u not supported\n"), areq);
 
662
                        return STATUS_ERROR;
 
663
        }
 
664
 
 
665
        return STATUS_OK;
 
666
}
 
667
 
 
668
/*
 
669
 * fe_setauthsvc
 
670
 * fe_getauthsvc
 
671
 *
 
672
 * Set/return the authentication service currently selected for use by the
 
673
 * frontend. (You can only use one in the frontend, obviously.)
 
674
 *
 
675
 * NB: This is not thread-safe if different threads try to select different
 
676
 * authentication services!  It's OK for fe_getauthsvc to select the default,
 
677
 * since that will be the same for all threads, but direct application use
 
678
 * of fe_setauthsvc is not thread-safe.  However, use of fe_setauthsvc is
 
679
 * deprecated anyway...
 
680
 */
 
681
 
 
682
static int      pg_authsvc = -1;
 
683
 
 
684
void
 
685
fe_setauthsvc(const char *name, char *PQerrormsg)
 
686
{
 
687
        int                     i;
 
688
 
 
689
        for (i = 0; i < n_authsvcs; ++i)
 
690
                if (strcmp(name, authsvcs[i].name) == 0)
 
691
                {
 
692
                        pg_authsvc = i;
 
693
                        break;
 
694
                }
 
695
        if (i == n_authsvcs)
 
696
        {
 
697
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
698
                                 libpq_gettext("invalid authentication service name \"%s\", ignored\n"),
 
699
                                 name);
 
700
        }
 
701
        return;
 
702
}
 
703
 
 
704
MsgType
 
705
fe_getauthsvc(char *PQerrormsg)
 
706
{
 
707
        if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
 
708
        {
 
709
                fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
 
710
                if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
 
711
                {
 
712
                        /* Can only get here if DEFAULT_CLIENT_AUTHSVC is misdefined */
 
713
                        return 0;
 
714
                }
 
715
        }
 
716
        return authsvcs[pg_authsvc].msgtype;
 
717
}
 
718
 
 
719
/*
 
720
 * fe_getauthname -- returns a pointer to dynamic space containing whatever
 
721
 *                                       name the user has authenticated to the system
 
722
 * if there is an error, return the error message in PQerrormsg
 
723
 */
 
724
char *
 
725
fe_getauthname(char *PQerrormsg)
 
726
{
 
727
        const char *name = NULL;
 
728
        char       *authn;
 
729
        MsgType         authsvc;
 
730
#ifdef WIN32
 
731
        char            username[128];
 
732
        DWORD           namesize = sizeof(username) - 1;
 
733
#else
 
734
        char            pwdbuf[BUFSIZ];
 
735
        struct passwd pwdstr;
 
736
        struct passwd *pw = NULL;
 
737
#endif
 
738
 
 
739
        authsvc = fe_getauthsvc(PQerrormsg);
 
740
 
 
741
        /* this just guards against broken DEFAULT_CLIENT_AUTHSVC, see above */
 
742
        if (authsvc == 0)
 
743
                return NULL;                    /* leave original error message in place */
 
744
 
 
745
        pglock_thread();
 
746
 
 
747
#ifdef KRB4
 
748
        if (authsvc == STARTUP_KRB4_MSG)
 
749
                name = pg_krb4_authname(PQerrormsg);
 
750
#endif
 
751
#ifdef KRB5
 
752
        if (authsvc == STARTUP_KRB5_MSG)
 
753
                name = pg_krb5_authname(PQerrormsg);
 
754
#endif
 
755
 
 
756
        if (authsvc == STARTUP_MSG
 
757
                || (authsvc == STARTUP_KRB4_MSG && !name)
 
758
                || (authsvc == STARTUP_KRB5_MSG && !name))
 
759
        {
 
760
#ifdef WIN32
 
761
                if (GetUserName(username, &namesize))
 
762
                        name = username;
 
763
#else
 
764
                if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pw) == 0)
 
765
                        name = pw->pw_name;
 
766
#endif
 
767
        }
 
768
 
 
769
        if (authsvc != STARTUP_MSG && authsvc != STARTUP_KRB4_MSG && authsvc != STARTUP_KRB5_MSG)
 
770
                snprintf(PQerrormsg, PQERRORMSG_LENGTH,
 
771
                                 libpq_gettext("fe_getauthname: invalid authentication system: %d\n"),
 
772
                                 authsvc);
 
773
 
 
774
        authn = name ? strdup(name) : NULL;
 
775
 
 
776
        pgunlock_thread();
 
777
 
 
778
        return authn;
 
779
}