basic_ncsa_auth.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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"
30 #include "rfc1738.h"
31 
32 #include <string>
33 #include <unordered_map>
34 #if HAVE_SYS_STAT_H
35 #include <sys/stat.h>
36 #endif
37 #if HAVE_CRYPT_H
38 #include <crypt.h>
39 #endif
40 
41 typedef std::unordered_map<std::string, std::string> usermap_t;
43 
44 static void
46 {
47  FILE *f;
48  char buf[HELPER_INPUT_BUFFER];
49  char *user;
50  char *passwd;
51 
52  usermap.clear();
53  //TODO: change to c++ streams
54  f = fopen(passwdfile, "r");
55  if (!f) {
56  int xerrno = errno;
57  fprintf(stderr, "FATAL: %s: %s\n", passwdfile, xstrerr(xerrno));
58  exit(EXIT_FAILURE);
59  }
60  unsigned int lineCount = 0;
61  buf[HELPER_INPUT_BUFFER-1] = '\0';
62  while (fgets(buf, sizeof(buf)-1, f) != nullptr) {
63  ++lineCount;
64  if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
65  (buf[0] == '\n'))
66  continue;
67  user = strtok(buf, ":\n\r");
68  if (user == nullptr) {
69  fprintf(stderr, "ERROR: Missing user name at %s line %d\n", passwdfile, lineCount);
70  continue;
71  }
72  passwd = strtok(nullptr, ":\n\r");
73  if ((strlen(user) > 0) && passwd) {
74  usermap[user] = passwd;
75  }
76  }
77  fclose(f);
78 }
79 
80 int
81 main(int argc, char **argv)
82 {
83  struct stat sb;
84  time_t change_time = -1;
85  char buf[HELPER_INPUT_BUFFER];
86  char *user, *passwd, *p;
87  setbuf(stdout, nullptr);
88  if (argc != 2) {
89  fprintf(stderr, "Usage: ncsa_auth <passwordfile>\n");
90  exit(EXIT_FAILURE);
91  }
92  if (stat(argv[1], &sb) != 0) {
93  fprintf(stderr, "FATAL: cannot stat %s\n", argv[1]);
94  exit(EXIT_FAILURE);
95  }
96  while (fgets(buf, HELPER_INPUT_BUFFER, stdin) != nullptr) {
97  if ((p = strchr(buf, '\n')) != nullptr)
98  *p = '\0'; /* strip \n */
99  if (stat(argv[1], &sb) == 0) {
100  if (sb.st_mtime != change_time) {
101  read_passwd_file(argv[1]);
102  change_time = sb.st_mtime;
103  }
104  }
105  if ((user = strtok(buf, " ")) == nullptr) {
106  SEND_ERR("");
107  continue;
108  }
109  if ((passwd = strtok(nullptr, "")) == nullptr) {
110  SEND_ERR("");
111  continue;
112  }
113  rfc1738_unescape(user);
114  rfc1738_unescape(passwd);
115  const auto userpassIterator = usermap.find(user);
116  if (userpassIterator == usermap.end()) {
117  SEND_ERR("No such user");
118  continue;
119  }
120  std::string stored_pass = userpassIterator->second;
121  const char *salted = stored_pass.c_str(); // locally stored version contains salt etc.
122 
123  char *crypted = nullptr;
124 #if HAVE_CRYPT
125  size_t passwordLength = strlen(passwd);
126  // Bug 3831: given algorithms more secure than DES crypt() does not truncate, so we can ignore the bug 3107 length checks below
127  // '$1$' = MD5, '$2a$' = Blowfish, '$5$' = SHA256 (Linux), '$6$' = SHA256 (BSD) and SHA512
128  if (passwordLength > 1 && salted[0] == '$' &&
129  (crypted = crypt(passwd, salted)) && stored_pass == crypted) {
130  SEND_OK("");
131  continue;
132  }
133  // 'other' prefixes indicate DES algorithm.
134  if (passwordLength <= 8 && (crypted = crypt(passwd, salted)) && stored_pass == crypted) {
135  SEND_OK("");
136  continue;
137  }
138  if (passwordLength > 8 && (crypted = crypt(passwd, salted)) && stored_pass == crypted) {
139  // Bug 3107: crypt() DES functionality silently truncates long passwords.
140  SEND_ERR("Password too long. Only 8 characters accepted.");
141  continue;
142  }
143 
144 #endif
145  if ( (crypted = crypt_md5(passwd, salted)) && stored_pass == crypted) {
146  SEND_OK("");
147  continue;
148  }
149  if ( (crypted = md5sum(passwd)) && stored_pass == crypted) {
150  SEND_OK("");
151  continue;
152  }
153  SEND_ERR("Wrong password");
154  }
155  return EXIT_SUCCESS;
156 }
157 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
char * crypt(const char *wort, const char *salt)
Definition: encrypt.c:240
usermap_t usermap
static void read_passwd_file(const char *passwdfile)
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
#define SEND_ERR(x)
char * crypt_md5(const char *pw, const char *salt)
Definition: crypt_md5.cc:54
static time_t change_time
Definition: text_backend.cc:45
int main(int argc, char **argv)
static char * passwdfile
Definition: text_backend.cc:43
#define HELPER_INPUT_BUFFER
Definition: UserRequest.cc:24
char * md5sum(const char *s)
Definition: crypt_md5.cc:183
std::unordered_map< std::string, std::string > usermap_t
#define SEND_OK(x)

 

Introduction

Documentation

Support

Miscellaneous