]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/basic_auth/NCSA/basic_ncsa_auth.cc
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / basic_auth / NCSA / basic_ncsa_auth.cc
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /*
10 * AUTHOR: Arjan de Vet <Arjan.deVet@adv.iae.nl>
11 *
12 * Example authentication program for Squid, based on the original
13 * proxy_auth code from client_side.c, written by
14 * Jon Thackray <jrmt@uk.gdscorp.com>.
15 *
16 * Uses a NCSA httpd style password file for authentication with the
17 * following improvements suggested by various people:
18 *
19 * - comment lines are possible and should start with a '#';
20 * - empty or blank lines are possible;
21 * - extra fields in the password file are ignored; this makes it
22 * possible to use a Unix password file but I do not recommend that.
23 *
24 * MD5 without salt and magic strings - Added by Ramon de Carvalho and Rodrigo Rubira Branco
25 */
26
27 #include "squid.h"
28 #include "crypt_md5.h"
29 #include "helpers/defines.h"
30 #include "rfc1738.h"
31
32 #include <unordered_map>
33 #if HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36 #if HAVE_CRYPT_H
37 #include <crypt.h>
38 #endif
39
40 typedef std::unordered_map<std::string, std::string> usermap_t;
41 usermap_t usermap;
42
43 static void
44 read_passwd_file(const char *passwdfile)
45 {
46 FILE *f;
47 char buf[HELPER_INPUT_BUFFER];
48 char *user;
49 char *passwd;
50
51 usermap.clear();
52 //TODO: change to c++ streams
53 f = fopen(passwdfile, "r");
54 if (NULL == f) {
55 fprintf(stderr, "FATAL: %s: %s\n", passwdfile, xstrerror());
56 exit(1);
57 }
58 unsigned int lineCount = 0;
59 buf[HELPER_INPUT_BUFFER-1] = '\0';
60 while (fgets(buf, sizeof(buf)-1, f) != NULL) {
61 ++lineCount;
62 if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
63 (buf[0] == '\n'))
64 continue;
65 user = strtok(buf, ":\n\r");
66 if (user == NULL) {
67 fprintf(stderr, "ERROR: Missing user name at %s line %d\n", passwdfile, lineCount);
68 continue;
69 }
70 passwd = strtok(NULL, ":\n\r");
71 if ((strlen(user) > 0) && passwd) {
72 usermap[user] = passwd;
73 }
74 }
75 fclose(f);
76 }
77
78 int
79 main(int argc, char **argv)
80 {
81 struct stat sb;
82 time_t change_time = -1;
83 char buf[HELPER_INPUT_BUFFER];
84 char *user, *passwd, *p;
85 setbuf(stdout, NULL);
86 if (argc != 2) {
87 fprintf(stderr, "Usage: ncsa_auth <passwordfile>\n");
88 exit(1);
89 }
90 if (stat(argv[1], &sb) != 0) {
91 fprintf(stderr, "FATAL: cannot stat %s\n", argv[1]);
92 exit(1);
93 }
94 while (fgets(buf, HELPER_INPUT_BUFFER, stdin) != NULL) {
95 if ((p = strchr(buf, '\n')) != NULL)
96 *p = '\0'; /* strip \n */
97 if (stat(argv[1], &sb) == 0) {
98 if (sb.st_mtime != change_time) {
99 read_passwd_file(argv[1]);
100 change_time = sb.st_mtime;
101 }
102 }
103 if ((user = strtok(buf, " ")) == NULL) {
104 SEND_ERR("");
105 continue;
106 }
107 if ((passwd = strtok(NULL, "")) == NULL) {
108 SEND_ERR("");
109 continue;
110 }
111 rfc1738_unescape(user);
112 rfc1738_unescape(passwd);
113 const auto userpassIterator = usermap.find(user);
114 if (userpassIterator == usermap.end()) {
115 SEND_ERR("No such user");
116 continue;
117 }
118 std::string stored_pass = userpassIterator->second;
119
120 char *crypted = NULL;
121 #if HAVE_CRYPT
122 size_t passwordLength = strlen(passwd);
123 // Bug 3831: given algorithms more secure than DES crypt() does not truncate, so we can ignore the bug 3107 length checks below
124 // '$1$' = MD5, '$2a$' = Blowfish, '$5$' = SHA256 (Linux), '$6$' = SHA256 (BSD) and SHA512
125 if (passwordLength > 1 && stored_pass[0] == '$' &&
126 (crypted = crypt(passwd, stored_pass.c_str())) && stored_pass == crypted) {
127 SEND_OK("");
128 continue;
129 }
130 // 'other' prefixes indicate DES algorithm.
131 if (passwordLength <= 8 && (crypted = crypt(passwd, stored_pass.c_str())) && stored_pass == crypted) {
132 SEND_OK("");
133 continue;
134 }
135 if (passwordLength > 8 && (crypted = crypt(passwd, stored_pass.c_str())) && stored_pass == crypted) {
136 // Bug 3107: crypt() DES functionality silently truncates long passwords.
137 SEND_ERR("Password too long. Only 8 characters accepted.");
138 continue;
139 }
140
141 #endif
142 if ( (crypted = crypt_md5(passwd, stored_pass.c_str())) && stored_pass == crypted) {
143 SEND_OK("");
144 continue;
145 }
146 if ( (crypted = md5sum(passwd)) && stored_pass == crypted) {
147 SEND_OK("");
148 continue;
149 }
150 SEND_ERR("Wrong password");
151 }
152 exit(0);
153 }
154