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