1 /*
2 * PostgreSQL type definitions for chkpass
3 * Written by D'Arcy J.M. Cain
4 * darcy@druid.net
5 * http://www.druid.net/darcy/
6 *
7 * contrib/chkpass/chkpass.c
8 * best viewed with tabs set to 4
9 */
10
11 #include "postgres.h"
12
13 #include <time.h>
14 #include <unistd.h>
15 #ifdef HAVE_CRYPT_H
16 #include <crypt.h>
17 #endif
18
19 #include "fmgr.h"
20 #include "utils/backend_random.h"
21 #include "utils/builtins.h"
22
23 PG_MODULE_MAGIC;
24
25 /*
26 * This type encrypts it's input unless the first character is a colon.
27 * The output is the encrypted form with a leading colon. The output
28 * format is designed to allow dump and reload operations to work as
29 * expected without doing special tricks.
30 */
31
32
33 /*
34 * This is the internal storage format for CHKPASSs.
35 * 15 is all I need but add a little buffer
36 */
37
38 typedef struct chkpass
39 {
40 char password[16];
41 } chkpass;
42
43
44 /* This function checks that the password is a good one
45 * It's just a placeholder for now */
46 static int
verify_pass(const char * str)47 verify_pass(const char *str)
48 {
49 return 0;
50 }
51
52 /*
53 * CHKPASS reader.
54 */
55 PG_FUNCTION_INFO_V1(chkpass_in);
56 Datum
chkpass_in(PG_FUNCTION_ARGS)57 chkpass_in(PG_FUNCTION_ARGS)
58 {
59 char *str = PG_GETARG_CSTRING(0);
60 chkpass *result;
61 char mysalt[4];
62 char *crypt_output;
63 static char salt_chars[] =
64 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
65
66 /* special case to let us enter encrypted passwords */
67 if (*str == ':')
68 {
69 result = (chkpass *) palloc0(sizeof(chkpass));
70 strlcpy(result->password, str + 1, 13 + 1);
71 PG_RETURN_POINTER(result);
72 }
73
74 if (verify_pass(str) != 0)
75 ereport(ERROR,
76 (errcode(ERRCODE_DATA_EXCEPTION),
77 errmsg("password \"%s\" is weak", str)));
78
79 result = (chkpass *) palloc0(sizeof(chkpass));
80
81 if (!pg_backend_random(mysalt, 2))
82 ereport(ERROR,
83 (errmsg("could not generate random salt")));
84
85 mysalt[0] = salt_chars[mysalt[0] & 0x3f];
86 mysalt[1] = salt_chars[mysalt[1] & 0x3f];
87 mysalt[2] = 0; /* technically the terminator is not necessary
88 * but I like to play safe */
89
90 crypt_output = crypt(str, mysalt);
91 if (crypt_output == NULL)
92 ereport(ERROR,
93 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
94 errmsg("crypt() failed")));
95
96 strlcpy(result->password, crypt_output, sizeof(result->password));
97
98 PG_RETURN_POINTER(result);
99 }
100
101 /*
102 * CHKPASS output function.
103 * Just like any string but we know it is max 15 (13 plus colon and terminator.)
104 */
105
106 PG_FUNCTION_INFO_V1(chkpass_out);
107 Datum
chkpass_out(PG_FUNCTION_ARGS)108 chkpass_out(PG_FUNCTION_ARGS)
109 {
110 chkpass *password = (chkpass *) PG_GETARG_POINTER(0);
111 char *result;
112
113 result = (char *) palloc(16);
114 result[0] = ':';
115 strlcpy(result + 1, password->password, 15);
116
117 PG_RETURN_CSTRING(result);
118 }
119
120
121 /*
122 * special output function that doesn't output the colon
123 */
124
125 PG_FUNCTION_INFO_V1(chkpass_rout);
126 Datum
chkpass_rout(PG_FUNCTION_ARGS)127 chkpass_rout(PG_FUNCTION_ARGS)
128 {
129 chkpass *password = (chkpass *) PG_GETARG_POINTER(0);
130
131 PG_RETURN_TEXT_P(cstring_to_text(password->password));
132 }
133
134
135 /*
136 * Boolean tests
137 */
138
139 PG_FUNCTION_INFO_V1(chkpass_eq);
140 Datum
chkpass_eq(PG_FUNCTION_ARGS)141 chkpass_eq(PG_FUNCTION_ARGS)
142 {
143 chkpass *a1 = (chkpass *) PG_GETARG_POINTER(0);
144 text *a2 = PG_GETARG_TEXT_PP(1);
145 char str[9];
146 char *crypt_output;
147
148 text_to_cstring_buffer(a2, str, sizeof(str));
149 crypt_output = crypt(str, a1->password);
150 if (crypt_output == NULL)
151 ereport(ERROR,
152 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
153 errmsg("crypt() failed")));
154
155 PG_RETURN_BOOL(strcmp(a1->password, crypt_output) == 0);
156 }
157
158 PG_FUNCTION_INFO_V1(chkpass_ne);
159 Datum
chkpass_ne(PG_FUNCTION_ARGS)160 chkpass_ne(PG_FUNCTION_ARGS)
161 {
162 chkpass *a1 = (chkpass *) PG_GETARG_POINTER(0);
163 text *a2 = PG_GETARG_TEXT_PP(1);
164 char str[9];
165 char *crypt_output;
166
167 text_to_cstring_buffer(a2, str, sizeof(str));
168 crypt_output = crypt(str, a1->password);
169 if (crypt_output == NULL)
170 ereport(ERROR,
171 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
172 errmsg("crypt() failed")));
173
174 PG_RETURN_BOOL(strcmp(a1->password, crypt_output) != 0);
175 }
176