summaryrefslogtreecommitdiff
path: root/daemon/basic.c
blob: a8325af0c8c738a254b204c6da25d6cf18af8d6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243

/* On some linux this is required to get crypt to show itself */
#define _XOPEN_SOURCE

#include "usuals.h"
#include "httpauthd.h"
#include "defaults.h"

#include <syslog.h>
#include <fcntl.h>
#include <unistd.h>

#define BASIC_MAXLINE 128

/* This needs to be the same as an MD5 hash length */
#define BASIC_HASH_KEY_LEN 16
#define BASIC_ESTABLISHED  (void*)1

/* -------------------------------------------------------------------------------
 * Structures
 */

typedef struct basic_context
{
  const char* filename;       /* The file name with the user names */
  const char* realm;          /* The realm for basic authentication */

  /* Context ----------------------------------------------------------- */
  hash_t* established;    /* Established connections */
}
basic_context_t;

/* -------------------------------------------------------------------------------
 * Handler Functions
 */

int basic_config(ha_context_t* context, const char* name, const char* value)
{
  basic_context_t* ctx = (basic_context_t*)(context.data);

  if(strcmp(name, "basicfile") == 0)
  {
    ctx->filename = value;
    return HA_OK;
  }

  else if(strcmp(name, "realm") == 0)
  {
    ctx->realm = value;
    return HA_OK;
  }

  return HA_FALSE;
}

int basic_init(ha_context_t* context)
{
  /* Don't do global init */
  if(!context)
    return HA_OK;

  basic_context_t* ctx = (basic_context_t*)(context.data);
  int fd;

  /* Make sure there are some types of authentication we can do */
  if(!(context->types & HA_TYPE_BASIC))
  {
    ha_messagex(LOG_ERR, "Basic module configured, but does not implement any "
                         "configured authentication type.");
    return HA_ERROR;
  }

  /* Check to make sure the file exists */
  if(!ctx->filename)
  {
    ha_messagex(LOG_ERR, "Basic configuration incomplete. "
                         "Must have a BasicFile configured.");
    return HA_ERROR;
  }

  fd = open(ctx->filename, O_RDONLY);
  if(fd == -1)
  {
    ha_message(LOG_ERR, "can't open file for basic authentication: %s", ctx->filename);
    return HA_ERROR;
  }

  close(fd);

  /* Initialize our cache table */
  if(!(ctx->established = hash_create(BASIC_HASH_KEY_LEN)))
  {
    ha_messagex(LOG_CRIT, "out of memory");
    return HA_ERROR;
  }

  return HA_OK;
}

int basic_process(ha_context_t* context, ha_request_t* req,
                  ha_response_t* resp, ha_buffer_t* buf)
{
  basic_context_t* ctx = (basic_context_t*)(context.data);
  const char* header;
  char* t;
  int found = 0;
  ha_basic_header_t basic;

  memset(&basic, 0, sizeof(basic));

  ha_lock(NULL);

    /* Purge the cache */
    hash_purge(ctx->established, time(NULL) - context->timeout);

  ha_unlock(NULL);


  /*
   * Init checked and made sure basic auth is enabled, so we
   * can take that for granted here.
   */

  header = ha_getheader(req, "Authorization", BASIC_PREFIX);
  if(header)
  {
    if(ha_parsebasic(header, buf, &basic) == HA_ERROR)
      return HA_ERROR;

    /* Check and see if this connection is in the cache */
    ha_lock(NULL);

      if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED)
      {
        hash_touch(ctx->established, basic.key);
        found = 1;
      }

    ha_unlock(NULL);
  }


  /* If we have a user name and password that wasn't in the cache */
  if(!found && basic.user && basic.user[0] &&
     basic.password && basic.password[0])
  {
    FILE* f;
    char line[BASIC_MAXLINE];

    f = fopen(ctx->filename, "r");
    if(!f)
    {
      ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename);
      resp->code = HA_SERVER_ERROR;
      return HA_FALSE;
    }

    /*
     * Note: There should be no returns or jumps between
     * this point and the closing of the file below.
     */

    /* Now look through the whole file */
    while(!feof(f))
    {
      fgets(line, BASIC_MAXLINE, f);

      if(ferror(f))
        ha_message(LOG_ERR, "error reading basic password file");

      t = strchr(line, ':');
      if(t)
      {
        /* Split the line */
        *t = 0;
        t++;

        /* Check the user */
        if(strcmp(line, basic.user) == 0)
        {
          /* Not sure if crypt is thread safe so we lock */
          ha_lock();

            /* Check the password */
            if(strcmp(crypt(basic.password, t), t) == 0)
              found = 1;

          ha_unlock();

          if(found)
            break;
        }
      }
    }

    fclose(f);
  }

  /* If we found a user then tell them what it was */
  if(found)
  {
    resp->code = HA_SERVER_ACCEPT;
    resp->detail = basic.user;

    /* We put this connection into the successful connections */
    if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED))
    {
      ha_messagex(LOG_CRIT, "out of memory");
      return HA_ERROR;
    }
  }

  /* Otherwise make the browser authenticate */
  else
  {
    resp->code = HA_SERVER_DECLINE;

    ha_bufnext(buf);
    ha_bufcat(buf, "BASIC realm=\"", ctx->realm ? ctx->realm : "",
                  "\"", NULL);

    ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf));
  }

  return HA_OK;
}


/* -------------------------------------------------------------------------------
 * Handler Definition
 */

ha_handler_t basic_handler =
{
  "Basic",                  /* The type */
  basic_init,               /* Initialization function */
  basic_destroy,            /* Uninitialization routine */
  basic_config,             /* Config routine */
  basic_process,            /* Processing routine */
  NULL,                     /* A default context */
  sizeof(basic_context_t)   /* Size of the context */
};