]> granicus.if.org Git - postgresql/blob - src/backend/libpq/crypt.c
6e6555503aa4b5b1102c8becebf2dd4171645b47
[postgresql] / src / backend / libpq / crypt.c
1 /*-------------------------------------------------------------------------
2  *
3  * crypt.c--
4  *        Look into pg_user and check the encrypted password with the one
5  *        passed in from the frontend.
6  *
7  * Modification History
8  *
9  * Dec 17, 1997 - Todd A. Brandys
10  *      Orignal Version Completed.
11  *
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #ifdef HAVE_CRYPT_H
21 #include <crypt.h>
22 #endif
23
24 #include "postgres.h"
25 #include "miscadmin.h"
26 #include "utils/nabstime.h"
27 #include "storage/fd.h"
28 #include "libpq/crypt.h"
29
30 char**     pwd_cache = NULL;
31 int        pwd_cache_count = 0;
32
33 /*-------------------------------------------------------------------------*/
34
35 char* crypt_getpwdfilename() {
36
37   static char*     pfnam = NULL;
38
39   if (!pfnam) {
40     pfnam = (char*)malloc(strlen(DataDir) + strlen(CRYPT_PWD_FILE) + 2);
41     sprintf(pfnam, "%s/%s", DataDir, CRYPT_PWD_FILE);
42   }
43
44   return pfnam;
45 }
46
47 /*-------------------------------------------------------------------------*/
48
49 char* crypt_getpwdreloadfilename() {
50
51   static char*     rpfnam = NULL;
52
53   if (!rpfnam) {
54     char*     pwdfilename;
55
56     pwdfilename = crypt_getpwdfilename();
57     rpfnam = (char*)malloc(strlen(pwdfilename) + strlen(CRYPT_PWD_RELOAD_SUFX) + 1);
58     sprintf(rpfnam, "%s%s", pwdfilename, CRYPT_PWD_RELOAD_SUFX);
59   }
60
61   return rpfnam;
62 }
63
64 /*-------------------------------------------------------------------------*/
65
66 static
67 FILE* crypt_openpwdfile() {
68   char*     filename;
69   FILE*     pwdfile;
70
71   filename = crypt_getpwdfilename();
72   pwdfile = AllocateFile(filename, "r");
73
74   return pwdfile;
75 }
76
77 /*-------------------------------------------------------------------------*/
78
79 static
80 int compar_user(const void* user_a, const void* user_b) {
81
82   int     min,
83           value;
84   char*   login_a;
85   char*   login_b;
86
87   login_a = *((char**)user_a);
88   login_b = *((char**)user_b);
89
90   /* We only really want to compare the user logins which are first.  We look
91    * for the first SEPSTR char getting the number of chars there are before it.
92    * We only need to compare to the min count from the two strings.
93    */
94   min = strcspn(login_a, CRYPT_PWD_FILE_SEPSTR);
95   value = strcspn(login_b, CRYPT_PWD_FILE_SEPSTR);
96   if (value < min)
97     min = value;
98
99   /* We add one to min so that the separator character is included in the
100    * comparison.  Why?  I believe this will prevent logins that are proper
101    * prefixes of other logins from being 'masked out'.  Being conservative!
102    */
103   return strncmp(login_a, login_b, min + 1);
104 }
105
106 /*-------------------------------------------------------------------------*/
107
108 static
109 void crypt_loadpwdfile() {
110
111   char*     filename;
112   int       result;
113   FILE*     pwd_file;
114   char      buffer[256];
115
116   filename = crypt_getpwdreloadfilename();
117   result = unlink(filename);
118
119   /* We want to delete the flag file before reading the contents of the pg_pwd
120    * file.  If result == 0 then the unlink of the reload file was successful.
121    * This means that a backend performed a COPY of the pg_user file to
122    * pg_pwd.  Therefore we must now do a reload.
123    */
124   if (!pwd_cache || !result) {
125     if (pwd_cache) {    /* free the old data only if this is a reload */
126       while (pwd_cache_count--) {
127         free((void*)pwd_cache[pwd_cache_count]);
128       }
129       free((void*)pwd_cache);
130       pwd_cache = NULL;
131       pwd_cache_count = 0;
132     }
133
134     if (!(pwd_file = crypt_openpwdfile()))
135       return;
136
137     /* Here is where we load the data from pg_pwd.
138      */
139     while (fgets(buffer, 256, pwd_file) != NULL) {
140       /* We must remove the return char at the end of the string, as this will
141        * affect the correct parsing of the password entry.
142        */
143       if (buffer[(result = strlen(buffer) - 1)] == '\n')
144         buffer[result] = '\0';
145
146       pwd_cache = (char**)realloc((void*)pwd_cache, sizeof(char*) * (pwd_cache_count + 1));
147       pwd_cache[pwd_cache_count++] = strdup(buffer);
148     }
149     fclose(pwd_file);
150
151     /* Now sort the entries in the cache for faster searching later.
152      */
153     qsort((void*)pwd_cache, pwd_cache_count, sizeof(char*), compar_user);
154   }
155 }
156
157 /*-------------------------------------------------------------------------*/
158
159 static
160 void crypt_parsepwdentry(char* buffer, char** pwd, char** valdate) {
161
162   char*    parse = buffer;
163   int      count,
164            i;
165
166   /* skip to the password field
167    */
168   for (i = 0; i < 6; i++)
169     parse += (strcspn(parse, CRYPT_PWD_FILE_SEPSTR) + 1);
170
171   /* store a copy of user password to return
172    */
173   count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
174   *pwd = (char*)malloc(count + 1);
175   strncpy(*pwd, parse, count);
176   (*pwd)[count] = '\0';
177   parse += (count + 1);
178
179   /* store a copy of date login becomes invalid
180    */
181   count = strcspn(parse, CRYPT_PWD_FILE_SEPSTR);
182   *valdate = (char*)malloc(count + 1);
183   strncpy(*valdate, parse, count);
184   (*valdate)[count] = '\0';
185   parse += (count + 1);
186 }
187
188 /*-------------------------------------------------------------------------*/
189
190 static
191 int crypt_getloginfo(const char* user, char** passwd, char** valuntil) {
192
193   char*     pwd;
194   char*     valdate;
195   void*     fakeout;
196
197   *passwd = NULL;
198   *valuntil = NULL;
199   crypt_loadpwdfile();
200
201   if (pwd_cache) {
202     char**    pwd_entry;
203     char      user_search[NAMEDATALEN + 2];
204
205     sprintf(user_search, "%s\t", user);
206     fakeout = (void*)&user_search;
207     if ((pwd_entry = (char**)bsearch((void*)&fakeout, (void*)pwd_cache, pwd_cache_count, sizeof(char*), compar_user))) {
208       crypt_parsepwdentry(*pwd_entry, &pwd, &valdate);
209       *passwd = pwd;
210       *valuntil = valdate;
211       return STATUS_OK;
212     }
213
214     return STATUS_OK;
215   }
216
217   return STATUS_ERROR;
218 }
219
220 /*-------------------------------------------------------------------------*/
221
222 MsgType crypt_salt(const char* user) {
223
224   char*     passwd;
225   char*     valuntil;
226
227   if (crypt_getloginfo(user, &passwd, &valuntil) == STATUS_ERROR)
228     return STARTUP_UNSALT_MSG;
229
230   if (passwd == NULL || *passwd == '\0' || !strcmp(passwd, "\\N")) {
231     if (passwd) free((void*)passwd);
232     if (valuntil) free((void*)valuntil);
233     return STARTUP_UNSALT_MSG;
234   }
235
236   free((void*)passwd);
237   if (valuntil) free((void*)valuntil);
238   return STARTUP_SALT_MSG;
239 }
240
241 /*-------------------------------------------------------------------------*/
242
243 int crypt_verify(Port* port, const char* user, const char* pgpass) {
244
245   char*            passwd;
246   char*            valuntil;
247   char*            crypt_pwd;
248   int              retval = STATUS_ERROR;
249   AbsoluteTime     vuntil,
250                    current;
251
252   if (crypt_getloginfo(user, &passwd, &valuntil) == STATUS_ERROR)
253     return STATUS_ERROR;
254
255   if (passwd == NULL || *passwd == '\0') {
256     if (passwd) free((void*)passwd);
257     if (valuntil) free((void*)valuntil);
258     return STATUS_ERROR;
259   }
260
261   crypt_pwd = crypt(passwd, port->salt);
262   if (!strcmp(pgpass, crypt_pwd)) {
263     /* check here to be sure we are not past valuntil
264      */
265     if (!valuntil)
266       vuntil = INVALID_ABSTIME;
267     else
268       vuntil = nabstimein(valuntil);
269     current = GetCurrentAbsoluteTime();
270     if (vuntil != INVALID_ABSTIME && vuntil < current)
271       retval = STATUS_ERROR;
272     else
273       retval = STATUS_OK;
274   }
275
276   free((void*)passwd);
277   if (valuntil) free((void*)valuntil);
278   
279   return retval;
280 }