]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/digest_auth/file/text_backend.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / helpers / digest_auth / file / text_backend.cc
1 /*
2 * text_backend.c
3 *
4 * AUTHOR: Robert Collins. Based on ncsa_auth.c by Arjan de Vet
5 * <Arjan.deVet@adv.iae.nl>
6 *
7 * Example digest auth text backend for Squid, based on the original
8 * proxy_auth code from client_side.c, written by
9 * Jon Thackray <jrmt@uk.gdscorp.com>.
10 *
11 * - comment lines are possible and should start with a '#';
12 * - empty or blank lines are possible;
13 * - file format is username:plaintext or username:realm:HA1
14 *
15 * To build a directory integrated backend, you need to be able to
16 * calculate the HA1 returned to squid. To avoid storing a plaintext
17 * password you can calculate MD5(username:realm:password) when the
18 * user changes their password, and store the tuple username:realm:HA1.
19 * then find the matching username:realm when squid asks for the
20 * HA1.
21 *
22 * This implementation could be improved by using such a triple for
23 * the file format. However storing such a triple does little to
24 * improve security: If compromised the username:realm:HA1 combination
25 * is "plaintext equivalent" - for the purposes of digest authentication
26 * they allow the user access. Password syncronisation is not tackled
27 * by digest - just preventing on the wire compromise.
28 *
29 * Copyright (c) 2003 Robert Collins <robertc@squid-cache.org>
30 */
31 #include "squid.h"
32
33 #include "text_backend.h"
34
35 static hash_table *hash = NULL;
36 static HASHFREE my_free;
37 static char *passwdfile = NULL;
38 static int ha1mode = 0;
39 static time_t change_time = 0;
40
41 typedef struct _user_data {
42 hash_link hash;
43 char *passwd;
44 char *ha1;
45 } user_data;
46
47 static void
48 my_free(void *p)
49 {
50 user_data *u = static_cast<user_data*>(p);
51 xfree(u->hash.key);
52 xfree(u->passwd);
53 xfree(u);
54 }
55
56 static void
57 read_passwd_file(const char *passwordFile, int isHa1Mode)
58 {
59 FILE *f;
60 char buf[8192];
61 user_data *u;
62 char *user;
63 char *passwd;
64 char *ha1 = NULL;
65 char *realm;
66
67 if (hash != NULL) {
68 hashFreeItems(hash, my_free);
69 }
70 /* initial setup */
71 hash = hash_create((HASHCMP *) strcmp, 7921, hash_string);
72 if (NULL == hash) {
73 fprintf(stderr, "digest_file_auth: cannot create hash table\n");
74 exit(1);
75 }
76 f = fopen(passwordFile, "r");
77 while (fgets(buf, 8192, f) != NULL) {
78 if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
79 (buf[0] == '\n'))
80 continue;
81 user = strtok(buf, ":\n");
82 realm = strtok(NULL, ":\n");
83 passwd = strtok(NULL, ":\n");
84 if (!passwd) {
85 passwd = realm;
86 realm = NULL;
87 }
88 if ((strlen(user) > 0) && passwd) {
89 if (strncmp(passwd, "{HHA1}", 6) == 0) {
90 ha1 = passwd + 6;
91 passwd = NULL;
92 } else if (isHa1Mode) {
93 ha1 = passwd;
94 passwd = NULL;
95 }
96 if (ha1 && strlen(ha1) != 32) {
97 /* We cannot accept plaintext passwords when using HA1 encoding,
98 * as the passwords may be output to cache.log if debugging is on.
99 */
100 fprintf(stderr, "digest_file_auth: ignoring invalid password for %s\n", user);
101 continue;
102 }
103 u = static_cast<user_data*>(xcalloc(1, sizeof(*u)));
104 if (realm) {
105 int len = strlen(user) + strlen(realm) + 2;
106 u->hash.key = malloc(len);
107 snprintf(static_cast<char*>(u->hash.key), len, "%s:%s", user, realm);
108 } else {
109 u->hash.key = xstrdup(user);
110 }
111 if (ha1)
112 u->ha1 = xstrdup(ha1);
113 else
114 u->passwd = xstrdup(passwd);
115 hash_join(hash, &u->hash);
116 }
117 }
118 fclose(f);
119 }
120
121 /* replace when changing the backend */
122 void
123 TextArguments(int argc, char **argv)
124 {
125 struct stat sb;
126 if (argc == 2)
127 passwdfile = argv[1];
128 if ((argc == 3) && !strcmp("-c", argv[1])) {
129 ha1mode = 1;
130 passwdfile = argv[2];
131 }
132 if (!passwdfile) {
133 fprintf(stderr, "Usage: digest_file_auth [OPTIONS] <passwordfile>\n");
134 fprintf(stderr, " -c accept digest hashed passwords rather than plaintext in passwordfile\n");
135 exit(1);
136 }
137 if (stat(passwdfile, &sb) != 0) {
138 fprintf(stderr, "cannot stat %s\n", passwdfile);
139 exit(1);
140 }
141 }
142
143 static const user_data *
144 GetPassword(RequestData * requestData)
145 {
146 user_data *u;
147 struct stat sb;
148 char buf[256];
149 int len;
150 if (stat(passwdfile, &sb) == 0) {
151 if (sb.st_mtime != change_time) {
152 read_passwd_file(passwdfile, ha1mode);
153 change_time = sb.st_mtime;
154 }
155 }
156 if (!hash)
157 return NULL;
158 len = snprintf(buf, sizeof(buf), "%s:%s", requestData->user, requestData->realm);
159 if (len >= static_cast<int>(sizeof(buf)))
160 return NULL;
161 u = (user_data*)hash_lookup(hash, buf);
162 if (u)
163 return u;
164 u = (user_data*)hash_lookup(hash, requestData->user);
165 return u;
166 }
167
168 void
169 TextHHA1(RequestData * requestData)
170 {
171 const user_data *u = GetPassword(requestData);
172 if (!u) {
173 requestData->error = -1;
174 return;
175 }
176 if (u->ha1) {
177 xstrncpy(requestData->HHA1, u->ha1, sizeof(requestData->HHA1));
178 } else {
179 HASH HA1;
180 DigestCalcHA1("md5", requestData->user, requestData->realm, u->passwd, NULL, NULL, HA1, requestData->HHA1);
181 }
182 }