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