Blame view

mailbox/mu_auth.c 13.8 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2
   Copyright (C) 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
3

4 5 6
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
7
   version 3 of the License, or (at your option) any later version.
8

9
   This library is distributed in the hope that it will be useful,
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
11 12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
13

14 15 16 17
   You should have received a copy of the GNU Lesser General
   Public License along with this library; if not, write to the
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301 USA */
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

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#ifdef HAVE_SHADOW_H
# include <shadow.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_SECURITY_PAM_APPL_H
# include <security/pam_appl.h>
#endif
#ifdef HAVE_CRYPT_H
# include <crypt.h>
#endif

Sergey Poznyakoff authored
43
#include <mailutils/argcv.h>
44 45 46 47 48
#include <mailutils/list.h>
#include <mailutils/iterator.h>
#include <mailutils/mailbox.h>
#include <mailutils/mu_auth.h>
#include <mailutils/error.h>
49
#include <mailutils/errno.h>
50
#include <mailutils/nls.h>
Sergey Poznyakoff authored
51 52 53 54
#include <mailutils/debug.h>

static mu_debug_t mu_auth_debug;

55
#define S(s) ((s) ? (s) : "(none)")
Sergey Poznyakoff authored
56
#define DEBUG_AUTH(a)                                                         \
57
     MU_DEBUG11 (mu_auth_debug, MU_DEBUG_TRACE,                               \
Sergey Poznyakoff authored
58 59 60 61 62 63 64 65 66 67 68 69 70
                       "source=%s, name=%s, passwd=%s, uid=%lu, gid=%lu, "    \
                       "gecos=%s, dir=%s, shell=%s, mailbox=%s, quota=%lu, "  \
                       "change_uid=%d\n",                                     \
                       S ((a)->source),                                       \
                       S ((a)->name),                                         \
                       S ((a)->passwd),                                       \
                       (unsigned long) (a)->uid,                              \
                       (unsigned long) (a)->gid,                              \
                       S ((a)->gecos),                                        \
                       S ((a)->dir),                                          \
                       S ((a)->shell),                                        \
                       S ((a)->mailbox),                                      \
                       (unsigned long) (a)->quota,                            \
71
                       (a)->change_uid)
Sergey Poznyakoff authored
72 73 74 75 76 77 78 79
     
mu_debug_t
mu_auth_set_debug (mu_debug_t debug)
{
  mu_debug_t prev = mu_auth_debug;
  mu_auth_debug = debug;
  return prev;
}
80

Sergey Poznyakoff authored
81

82 83 84
/* memory allocation */
int 
mu_auth_data_alloc (struct mu_auth_data **ptr,
Sergey Poznyakoff authored
85 86 87 88 89 90 91 92 93
                    const char *name,
                    const char *passwd,
                    uid_t uid,
                    gid_t gid,
                    const char *gecos,
                    const char *dir,
                    const char *shell,
                    const char *mailbox,
                    int change_uid)
94
{
95
  size_t size;
96
  char *p;
97 98 99 100 101 102 103 104 105 106 107 108 109 110
  char *tmp_mailbox_name = NULL;
  
  if (!name)
    return EINVAL;
  if (!passwd)
    passwd = "x";
  if (!gecos)
    gecos = "";
  if (!dir)
    dir = "/nonexisting";
  if (!shell)
    shell = "/dev/null";
  if (!mailbox)
    {
Sergey Poznyakoff authored
111
      int rc = mu_construct_user_mailbox_url (&tmp_mailbox_name, name);
112 113
      if (rc)
	return rc;
Sergey Poznyakoff authored
114
      mailbox = tmp_mailbox_name;
115 116
    }

Sergey Poznyakoff authored
117 118 119 120 121 122 123
  size = sizeof (**ptr) +
         strlen (name) + 1 +
         strlen (passwd) + 1 +
         strlen (gecos) + 1 +
         strlen (dir) + 1 +
         strlen (shell) + 1 +
         strlen (mailbox) + 1;
124
  
125
  *ptr = calloc (1, size);
126
  if (!*ptr)
127
    return ENOMEM;
128 129 130 131

  p = (char *)(*ptr + 1);
  
#define COPY(f) \
132 133 134
  (*ptr)->f = p; \
  strcpy (p, f); \
  p += strlen (f) + 1
135 136 137 138 139 140 141 142 143 144
  
  COPY (name);
  COPY (passwd);
  COPY (gecos);
  COPY (dir);
  COPY (shell);
  COPY (mailbox);
  (*ptr)->uid = uid;
  (*ptr)->gid = gid;
  (*ptr)->change_uid = change_uid;
145 146

  free (tmp_mailbox_name);
147 148 149 150
  return 0;
}

void
151 152 153 154 155 156 157
mu_auth_data_set_quota (struct mu_auth_data *ptr, mu_off_t q)
{
  ptr->flags |= MU_AF_QUOTA;
  ptr->quota = q;
}

void
158 159 160 161 162
mu_auth_data_free (struct mu_auth_data *ptr)
{
  free (ptr);
}

163 164 165 166 167 168 169 170 171 172
void
mu_auth_data_destroy (struct mu_auth_data **pptr)
{
  if (pptr)
    {
      mu_auth_data_free (*pptr);
      *pptr = NULL;
    }
}

173 174 175 176 177 178 179 180 181
/* Generic functions */

struct auth_stack_entry {
  char *name;        /* for diagnostics purposes */
  mu_auth_fp fun;
  void *func_data;
};

void
182
mu_insert_stack_entry (mu_list_t *pflist, struct auth_stack_entry *entry)
183
{
184
  if (!*pflist && mu_list_create (pflist))
185
    return;
186
  mu_list_append (*pflist, entry);
187 188 189
}

int
190
mu_auth_runlist (mu_list_t flist, struct mu_auth_data **return_data,
Sergey Poznyakoff authored
191
                 const void *key, void *data)
192
{
193 194
  int status = MU_ERR_AUTH_FAILURE;
  int rc;
195
  mu_iterator_t itr;
196

197
  if (mu_list_get_iterator (flist, &itr) == 0)
198 199 200
    {
      struct auth_stack_entry *ep;
      
201
      for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
Sergey Poznyakoff authored
202 203 204
           mu_iterator_next (itr))
        {
          mu_iterator_current (itr, (void **)&ep);
205
          MU_DEBUG1 (mu_auth_debug, MU_DEBUG_TRACE, "Trying %s...", ep->name);
Sergey Poznyakoff authored
206
          rc = ep->fun (return_data, key, ep->func_data, data);
207 208
          MU_DEBUG2 (mu_auth_debug, MU_DEBUG_TRACE, 
                     "result: %d=%s\n", rc, mu_strerror (rc));
Sergey Poznyakoff authored
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
          if (rc == 0)
            {
              if (return_data)
                {
                  struct mu_auth_data *auth = *return_data;
                  if (auth->source == NULL)
                    auth->source = ep->name;
                  DEBUG_AUTH (auth);
                }
              status = rc;
              break;
            }
          else if (status != EAGAIN)
            status = rc;
        }
224

225
      mu_iterator_destroy (&itr);
226
    }
227
  return status;
228 229 230
}

int
231
mu_auth_nosupport (struct mu_auth_data **return_data MU_ARG_UNUSED,
Sergey Poznyakoff authored
232 233 234
                   const void *key MU_ARG_UNUSED,
                   void *func_data MU_ARG_UNUSED,
                   void *call_data MU_ARG_UNUSED)
235
{
236
  return ENOSYS;
237 238 239 240
}

/* II. Authorization: retrieving information about user */

241 242
static mu_list_t mu_auth_by_name_list, _tmp_auth_by_name_list;
static mu_list_t mu_auth_by_uid_list, _tmp_auth_by_uid_list;
243

244
int
245
mu_get_auth (struct mu_auth_data **auth, enum mu_auth_key_type type,
Sergey Poznyakoff authored
246
             const void *key)
247 248 249 250 251 252 253 254
{
  mu_list_t list;
  
  if (!mu_auth_by_name_list)
    mu_auth_begin_setup ();
  switch (type)
    {
    case mu_auth_key_name:
255 256 257
      MU_DEBUG1 (mu_auth_debug, MU_DEBUG_TRACE, 
                 "Getting auth info for user %s\n", 
                 (char*) key);
258 259 260 261
      list = mu_auth_by_name_list;
      break;

    case mu_auth_key_uid:
262 263 264
      MU_DEBUG1 (mu_auth_debug, MU_DEBUG_TRACE, 
                 "Getting auth info for UID %lu\n",
	         (unsigned long) *(uid_t*) key);
265 266 267 268
      list = mu_auth_by_uid_list;
      break;

    default:
269 270
      MU_DEBUG1 (mu_auth_debug, MU_DEBUG_ERROR, 
                 "Unknown mu_auth_key_type: %d\n", type);
271 272 273 274 275
      return EINVAL;
    }
  return mu_auth_runlist (list, auth, key, NULL);
}

276 277 278 279
struct mu_auth_data *
mu_get_auth_by_name (const char *username)
{
  struct mu_auth_data *auth = NULL;
280
  mu_get_auth (&auth, mu_auth_key_name, username);
281 282 283 284 285 286 287
  return auth;
}

struct mu_auth_data *
mu_get_auth_by_uid (uid_t uid)
{
  struct mu_auth_data *auth = NULL;
288
  mu_get_auth (&auth, mu_auth_key_uid, &uid);
289 290 291 292 293
  return auth;
}

/* III. Authentication: determining the authenticity of a user */

294
static mu_list_t mu_authenticate_list, _tmp_authenticate_list;
295 296 297 298

int
mu_authenticate (struct mu_auth_data *auth_data, char *pass)
{
Sergey Poznyakoff authored
299 300
  if (!auth_data)
    return EINVAL;
301 302 303
  MU_DEBUG2 (mu_auth_debug, MU_DEBUG_TRACE, 
             "mu_authenticate, user %s, source %s\n", 
             auth_data->name, auth_data->source);
304 305
  if (!mu_authenticate_list)
    mu_auth_begin_setup ();
306 307 308
  return mu_auth_runlist (mu_authenticate_list, NULL, auth_data, pass);
}

309 310

/* ************************************************************************* */
311 312 313 314 315 316 317

struct _module_handler {
  struct auth_stack_entry authenticate;
  struct auth_stack_entry auth_by_name;
  struct auth_stack_entry auth_by_uid;
};

318
static mu_list_t module_handler_list;
319 320 321 322 323 324

void
mu_auth_register_module (struct mu_auth_module *mod)
{
  struct _module_handler *entry;
  
325 326
  if (mod->init)
    mu_gocs_register (mod->name, mod->init);
327
  
328
  if (!module_handler_list && mu_list_create (&module_handler_list))
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
    abort ();

  entry = malloc (sizeof (*entry));
  if (!entry)
    {
      mu_error ("not enough memory");
      exit (1);
    }
  entry->authenticate.name = mod->name;
  entry->authenticate.fun  = mod->authenticate;
  entry->authenticate.func_data = mod->authenticate_data;
  entry->auth_by_name.name = mod->name;
  entry->auth_by_name.fun = mod->auth_by_name;
  entry->auth_by_name.func_data = mod->auth_by_name_data;
  entry->auth_by_uid.name = mod->name;
  entry->auth_by_uid.fun = mod->auth_by_uid;
  entry->auth_by_uid.func_data = mod->auth_by_uid_data;
    
347
  mu_list_append (module_handler_list, entry);
348 349 350 351 352 353 354
  
}

static struct _module_handler *
_locate (const char *name)
{
  struct _module_handler *rp = NULL;
355
  mu_iterator_t itr;
356

357
  if (mu_list_get_iterator (module_handler_list, &itr) == 0)
358 359 360
    {
      struct _module_handler *p;
      
361
      for (mu_iterator_first (itr); !rp && !mu_iterator_is_done (itr);
Sergey Poznyakoff authored
362 363 364 365 366 367
           mu_iterator_next (itr))
        {
          mu_iterator_current (itr, (void **)&p);
          if (strcmp (p->authenticate.name, name) == 0)
            rp = p;
        }
368

369
      mu_iterator_destroy (&itr);
370 371 372 373 374 375 376
    }
  return rp;
}

static void
_add_module_list (const char *modlist, int (*fun)(const char *name))
{
377 378 379
  int argc;
  char **argv;
  int rc, i;
380
  
381 382
  rc = mu_argcv_get (modlist, ":", NULL, &argc, &argv);
  if (rc)
383
    {
384 385 386 387
      mu_error (_("cannot split line `%s': %s"), modlist, mu_strerror (rc));
      exit (1);
    }

388
  for (i = 0; i < argc; i += 2)
389 390
    {
      if (fun (argv[i]))
Sergey Poznyakoff authored
391 392 393 394 395 396 397 398 399 400
        {
          /* Historically,auth functions used ENOENT. We support this
             return value for backward compatibility. */
          if (errno == ENOENT || errno == MU_ERR_NOENT)
            mu_error (_("no such module: %s"), argv[i]);
          else
            mu_error (_("failed to add module %s: %s"),
                      argv[i], strerror (errno));
          exit (1);
        }
401
    }
402
  mu_argcv_free (argc, argv);
403 404
}

405

406 407 408 409 410 411 412
int
mu_authorization_add_module (const char *name)
{
  struct _module_handler *mod = _locate (name);
  
  if (!mod)
    {
Sergey Poznyakoff authored
413
      errno = MU_ERR_NOENT;
414 415
      return 1;
    }
416 417
  mu_insert_stack_entry (&_tmp_auth_by_name_list, &mod->auth_by_name);
  mu_insert_stack_entry (&_tmp_auth_by_uid_list, &mod->auth_by_uid);
418 419 420 421 422 423 424 425 426
  return 0;
}

void
mu_authorization_add_module_list (const char *modlist)
{
  _add_module_list (modlist, mu_authorization_add_module);
}

427 428 429 430 431 432 433 434
void
mu_authorization_clear_list ()
{
  mu_list_destroy (&_tmp_auth_by_name_list);
  mu_list_destroy (&_tmp_auth_by_uid_list);
}


435 436 437 438 439 440 441
int
mu_authentication_add_module (const char *name)
{
  struct _module_handler *mod = _locate (name);

  if (!mod)
    {
Sergey Poznyakoff authored
442
      errno = MU_ERR_NOENT;
443 444
      return 1;
    }
445
  mu_insert_stack_entry (&_tmp_authenticate_list, &mod->authenticate);
446 447 448 449 450 451 452 453 454
  return 0;
}

void
mu_authentication_add_module_list (const char *modlist)
{
  _add_module_list (modlist, mu_authentication_add_module);
}

455 456 457 458 459 460 461
void
mu_authentication_clear_list ()
{
  mu_list_destroy (&_tmp_authenticate_list);
}


462 463
/* ************************************************************************ */

464 465 466 467 468 469 470
/* Setup functions. Note that:
   1) If libmuath is not linked in, then "generic" and "system" modules
      are used unconditionally. This provides compatibility with the
      standard getpw.* functions.
   2) --authentication and --authorization modify only temporary lists,
      which get flushed to the main ones when mu_auth_finish_setup() is
      run. Thus, the default "generic:system" remain in force until
471
      argp_parse() exits. */
472
   
473
void
474
mu_auth_begin_setup ()
475
{
476
  mu_iterator_t itr;
477

478 479 480 481 482 483
  if (!module_handler_list)
    {
      mu_auth_register_module (&mu_auth_generic_module); 
      mu_auth_register_module (&mu_auth_system_module);
    }
  
484 485
  if (!mu_authenticate_list)
    {
486
      if (mu_list_get_iterator (module_handler_list, &itr) == 0)
Sergey Poznyakoff authored
487 488 489 490 491 492 493 494 495 496 497 498
        {
          struct _module_handler *mod;
          
          for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
               mu_iterator_next (itr))
            {
              mu_iterator_current (itr, (void **)&mod);
              mu_insert_stack_entry (&mu_authenticate_list,
                                     &mod->authenticate);
            }
          mu_iterator_destroy (&itr);
        }
499 500 501 502
    }

  if (!mu_auth_by_name_list)
    {
503
      if (mu_list_get_iterator (module_handler_list, &itr) == 0)
Sergey Poznyakoff authored
504 505 506 507 508 509 510 511 512 513 514 515
        {
          struct _module_handler *mod;
          
          for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
               mu_iterator_next (itr))
            {
              mu_iterator_current (itr, (void **)&mod);
              mu_insert_stack_entry (&mu_auth_by_name_list, &mod->auth_by_name);
              mu_insert_stack_entry (&mu_auth_by_uid_list, &mod->auth_by_uid);
            }
          mu_iterator_destroy (&itr);
        }
516 517 518 519
    }
}

void
520 521
mu_auth_finish_setup ()
{
522 523 524
  mu_list_destroy (&mu_authenticate_list);
  mu_list_destroy (&mu_auth_by_name_list);
  mu_list_destroy (&mu_auth_by_uid_list);
525 526 527 528 529 530 531 532 533 534 535
  
  mu_authenticate_list = _tmp_authenticate_list;
  _tmp_authenticate_list = NULL;
  mu_auth_by_name_list = _tmp_auth_by_name_list;
  _tmp_auth_by_name_list = NULL;
  mu_auth_by_uid_list = _tmp_auth_by_uid_list;
  _tmp_auth_by_uid_list = NULL;
  
  mu_auth_begin_setup ();
}

536