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