summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-08-12 06:37:51 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-08-12 06:37:51 +0000
commit2d1b5a5e83edea9395e43cabc6455b6a57d4188b (patch)
tree2e17f4f3e253512e9676ecb532d89ab4edbfd3a1
parentb5423ab5aaae372d7864ab94aca620f38abd9122 (diff)
downloadmailutils-2d1b5a5e83edea9395e43cabc6455b6a57d4188b.tar.gz
mailutils-2d1b5a5e83edea9395e43cabc6455b6a57d4188b.tar.bz2
Radius authentication/authorization.
-rw-r--r--auth/radius.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/auth/radius.c b/auth/radius.c
new file mode 100644
index 000000000..8a5f09754
--- /dev/null
+++ b/auth/radius.c
@@ -0,0 +1,571 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ 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
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ 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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/mailbox.h>
+#include <mailutils/argp.h>
+#include <mailutils/argcv.h>
+#include <mailutils/mu_auth.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+
+#ifdef ENABLE_RADIUS
+
+#include <radius/radius.h>
+
+#define ARG_AUTH_REQUEST 256
+#define ARG_GETPWNAM_REQUEST 257
+#define ARG_GETPWUID_REQUEST 258
+#define ARG_RADIUS_DIR 259
+
+static struct argp_option mu_radius_argp_option[] = {
+ { "radius-auth-request", ARG_AUTH_REQUEST, N_("REQUEST"), 0,
+ N_("Radius request to authenitcate the user"), 0 },
+ { "radius-getpwnam-request", ARG_GETPWNAM_REQUEST, N_("REQUEST"), 0,
+ N_("Radius request to retrieve a passwd entry based on username"), 0 },
+ { "radius-getpwuid-request", ARG_GETPWUID_REQUEST, N_("REQUEST"), 0,
+ N_("Radius request to retrieve a passwd entry based on UID"), 0 },
+ { "radius-directory", ARG_RADIUS_DIR, N_("DIR"), 0,
+ N_("Set path to the radius configuration directory"), 0 },
+ { NULL }
+};
+
+static char *auth_request_str;
+static grad_avp_t *auth_request;
+
+static char *getpwnam_request_str;
+static grad_avp_t *getpwnam_request;
+
+static char *getpwuid_request_str;
+static grad_avp_t *getpwuid_request;
+
+static int MU_User_Name;
+static int MU_UID;
+static int MU_GID;
+static int MU_GECOS;
+static int MU_Dir;
+static int MU_Shell;
+static int MU_Mailbox;
+
+void
+get_attribute (int *pattr, char *name, struct argp_state *state)
+{
+ grad_dict_attr_t *attr = grad_attr_name_to_dict (name);
+ if (!attr)
+ argp_error (state, _("Radius attribute %s not defined"), name);
+ *pattr = attr->value;
+}
+
+enum parse_state
+ {
+ state_lhs,
+ state_op,
+ state_rhs,
+ state_delim
+ };
+
+int
+parse_pairlist (grad_avp_t **plist, const char *input,
+ struct argp_state *argp_state)
+{
+ int rc;
+ int i, argc;
+ char **argv;
+ enum parse_state state;
+ grad_locus_t loc;
+ char *name;
+ char *op; /* FIXME: It is actually ignored. Should it be? */
+
+ if (!input)
+ return 1;
+
+ if ((rc = argcv_get (input, ",", NULL, &argc, &argv)))
+ argp_error (argp_state, _("Cannot parse input `%s': %s"),
+ input, mu_strerror (rc));
+
+ loc.file = "<command line>"; /* FIXME */
+ loc.line = 0;
+
+ for (i = 0, state = state_lhs; i < argc; i++)
+ {
+ grad_avp_t *pair;
+
+ switch (state)
+ {
+ case state_lhs:
+ name = argv[i];
+ state = state_op;
+ break;
+
+ case state_op:
+ op = argv[i];
+ state = state_rhs;
+ break;
+
+ case state_rhs:
+ loc.line = i; /* Just to keep track of error location */
+ pair = grad_create_pair (&loc, name, grad_operator_equal, argv[i]);
+ if (!pair)
+ argp_error (argp_state, _("cannot create radius A/V pair `%s'"),
+ name);
+ grad_avl_merge (plist, &pair);
+ state = state_delim;
+ break;
+
+ case state_delim:
+ if (strcmp (argv[i], ","))
+ argp_error (argp_state, _("expected `,' but found `%s'"), argv[i]);
+ state = state_lhs;
+ }
+ }
+
+ if (state != state_delim && state != state_delim)
+ argp_error (argp_state, _("malformed radius A/V list"));
+
+ argcv_free (argc, argv);
+ return 0;
+}
+
+static void
+init (struct argp_state *state)
+{
+ grad_path_init ();
+ srand (time (NULL) + getpid ());
+
+ if (grad_dict_init ())
+ argp_error (state, _("Cannot read radius dictionaries"));
+
+ /* Check whether mailutils attributes are defined */
+ get_attribute (&MU_User_Name, "MU-User-Name", state);
+ get_attribute (&MU_UID, "MU-UID", state);
+ get_attribute (&MU_GID, "MU-GID", state);
+ get_attribute (&MU_GECOS, "MU-GECOS", state);
+ get_attribute (&MU_Dir, "MU-Dir", state);
+ get_attribute (&MU_Shell, "MU-Shell", state);
+ get_attribute (&MU_Mailbox, "MU-Mailbox", state);
+
+ /* Parse saved requests */
+ parse_pairlist (&auth_request, auth_request_str, state);
+ parse_pairlist (&getpwnam_request, getpwnam_request_str, state);
+ parse_pairlist (&getpwuid_request, getpwuid_request_str, state);
+}
+
+static error_t
+mu_radius_argp_parser (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARG_AUTH_REQUEST:
+ auth_request_str = arg;
+ break;
+
+ case ARG_GETPWNAM_REQUEST:
+ getpwnam_request_str = arg;
+ break;
+
+ case ARG_GETPWUID_REQUEST:
+ getpwuid_request_str = arg;
+ break;
+
+ case ARG_RADIUS_DIR:
+ radius_dir = grad_estrdup(arg);
+ break;
+
+ case ARGP_KEY_FINI:
+ init (state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+char *
+_expand_query (const char *query, const char *ustr, const char *passwd)
+{
+ char *p, *q, *res;
+ int len;
+
+ if (!query)
+ return NULL;
+
+ /* Compute resulting query length */
+ for (len = 0, p = (char *) query; *p; )
+ {
+ if (*p == '%')
+ {
+ if (p[1] == 'u')
+ {
+ len += ustr ? strlen (ustr) : 2;
+ p += 2;
+ }
+ else if (p[1] == 'p')
+ {
+ len += passwd ? strlen (passwd) : 2;
+ p += 2;
+ }
+ else if (p[1] == '%')
+ {
+ len++;
+ p += 2;
+ }
+ else
+ {
+ len++;
+ p++;
+ }
+ }
+ else
+ {
+ len++;
+ p++;
+ }
+ }
+
+ res = grad_emalloc (len + 1);
+ if (!res)
+ return res;
+
+ for (p = (char *) query, q = res; *p; )
+ {
+ if (*p == '%')
+ {
+ switch (*++p)
+ {
+ case 'u':
+ if (ustr)
+ {
+ strcpy (q, ustr);
+ q += strlen (q);
+ }
+ else
+ {
+ *q++ = '%';
+ *q++ = 'u';
+ }
+ p++;
+ break;
+
+ case 'p':
+ if (passwd)
+ {
+ strcpy (q, passwd);
+ q += strlen (q);
+ }
+ else
+ {
+ *q++ = '%';
+ *q++ = 'u';
+ }
+ p++;
+ break;
+
+ case '%':
+ *q++ = *p++;
+ break;
+
+ default:
+ *q++ = *p++;
+ }
+ }
+ else
+ *q++ = *p++;
+ }
+ *q = 0;
+ return res;
+}
+
+
+static grad_avp_t *
+create_request (grad_avp_t *template, const char *ustr, const char *passwd)
+{
+ grad_avp_t *newp, *p;
+
+ newp = grad_avl_dup (template);
+ for (p = newp; p; p = p->next)
+ {
+ if (p->type == GRAD_TYPE_STRING)
+ {
+ char *value = _expand_query (p->avp_strvalue, ustr, passwd);
+ grad_free (p->avp_strvalue);
+ p->avp_strvalue = value;
+ p->avp_strlength = strlen (value);
+ }
+ }
+ return newp;
+}
+
+
+
+grad_request_t *
+send_request (grad_avp_t *pairs, int code,
+ const char *user, const char *passwd)
+{
+ grad_avp_t *plist = create_request (pairs, user, passwd);
+ if (plist)
+ {
+ grad_server_queue_t *queue = grad_client_create_queue (1, 0, 0);
+ grad_request_t *reply = grad_client_send (queue,
+ GRAD_PORT_AUTH, code,
+ plist);
+ grad_client_destroy_queue (queue);
+ grad_avl_free (plist);
+ return reply;
+ }
+ return NULL;
+}
+
+#define DEFAULT_HOME_PREFIX "/home/"
+#define DEFAULT_SHELL "/dev/null"
+
+int
+decode_reply (grad_request_t *reply, const char *user_name, char *password,
+ struct mu_auth_data **return_data)
+{
+ grad_avp_t *p;
+ int rc;
+
+ uid_t uid = -1;
+ gid_t gid = -1;
+ char *gecos = "RADIUS User";
+ char *dir = NULL;
+ char *shell = NULL;
+ char *mailbox = NULL;
+
+ p = grad_avl_find (reply->avlist, MU_User_Name);
+ if (p)
+ user_name = p->avp_strvalue;
+
+ p = grad_avl_find (reply->avlist, MU_UID);
+ if (p)
+ uid = p->avp_lvalue;
+ else
+ {
+ mu_error (_("Radius did not return UID for `%s'"), user_name);
+ return -1;
+ }
+
+ p = grad_avl_find (reply->avlist, MU_GID);
+ if (p)
+ gid = p->avp_lvalue;
+ else
+ {
+ mu_error (_("Radius did not return GID for `%s'"), user_name);
+ return -1;
+ }
+
+ p = grad_avl_find (reply->avlist, MU_GECOS);
+ if (p)
+ gecos = p->avp_strvalue;
+
+ p = grad_avl_find (reply->avlist, MU_Dir);
+ if (p)
+ dir = strdup (p->avp_strvalue);
+ else /* Try to provide a reasonable default */
+ {
+ dir = malloc (sizeof DEFAULT_HOME_PREFIX + strlen (user_name));
+ if (!dir) /* FIXME: Error code */
+ return 1;
+ strcat (strcpy (dir, DEFAULT_HOME_PREFIX), user_name);
+ }
+
+ p = grad_avl_find (reply->avlist, MU_Shell);
+ if (p)
+ shell = p->avp_strvalue;
+ else
+ shell = DEFAULT_SHELL;
+
+ p = grad_avl_find (reply->avlist, MU_Mailbox);
+ if (p)
+ mailbox = strdup (p->avp_strvalue);
+ else
+ {
+ rc = mu_construct_user_mailbox_url (&mailbox, user_name);
+ if (rc)
+ return rc;
+ }
+
+ rc = mu_auth_data_alloc (return_data,
+ user_name,
+ password,
+ uid,
+ gid,
+ gecos,
+ dir,
+ shell,
+ mailbox,
+ 1);
+
+ free (dir);
+ free (mailbox);
+ return rc;
+}
+
+int
+mu_radius_authenticate (struct mu_auth_data **return_data ARG_UNUSED,
+ const void *key,
+ void *func_data ARG_UNUSED, void *call_data)
+{
+ int rc;
+ grad_request_t *reply;
+ const struct mu_auth_data *auth_data = key;
+
+ if (!auth_request)
+ {
+ mu_error (_("--radius-auth-request is not specified"));
+ return 1;
+ }
+
+ reply = send_request (auth_request, RT_ACCESS_REQUEST,
+ auth_data->name, (char*) call_data);
+ rc = !reply || reply->code != RT_ACCESS_ACCEPT;
+ grad_request_free (reply);
+ return rc;
+}
+
+static int
+mu_auth_radius_user_by_name (struct mu_auth_data **return_data,
+ const void *key,
+ void *unused_func_data, void *unused_call_data)
+{
+ int rc = 1;
+ grad_request_t *reply;
+
+ if (!getpwnam_request)
+ {
+ mu_error (_("--radius-getpwnam-request is not specified"));
+ return 1;
+ }
+
+ reply = send_request (getpwnam_request, RT_ACCESS_REQUEST, key, NULL);
+ if (!reply)
+ mu_error (_("radius server did not respond"));
+ else if (reply->code != RT_ACCESS_ACCEPT)
+ mu_error (_("%s: server returned %s"),
+ (char*) key,
+ grad_request_code_to_name (reply->code));
+ else
+ rc = decode_reply (reply, key, "x", return_data);
+ grad_request_free (reply);
+ return rc;
+}
+
+static int
+mu_auth_radius_user_by_uid (struct mu_auth_data **return_data,
+ const void *key,
+ void *func_data, void *call_data)
+{
+ int rc = 1;
+ grad_request_t *reply;
+ char uidstr[64];
+
+ if (!key)
+ {
+ errno = EINVAL;
+ return 1;
+ }
+
+ if (!getpwuid_request)
+ {
+ mu_error (_("--radius-getpwuid-request is not specified"));
+ return 1;
+ }
+
+ snprintf (uidstr, sizeof (uidstr), "%u", *(uid_t*)key);
+ reply = send_request (getpwuid_request, RT_ACCESS_REQUEST, uidstr, NULL);
+ if (reply->code != RT_ACCESS_ACCEPT)
+ {
+ mu_error (_("uid %s: server returned %s"), uidstr,
+ grad_request_code_to_name (reply->code));
+ }
+ else
+ {
+ rc = decode_reply (reply, uidstr, "x", return_data);
+ }
+ grad_request_free (reply);
+ return rc;
+}
+
+struct argp mu_radius_argp = {
+ mu_radius_argp_option,
+ mu_radius_argp_parser,
+};
+
+#else
+static int
+mu_radius_authenticate (struct mu_auth_data **return_data ARG_UNUSED,
+ const void *key,
+ void *func_data ARG_UNUSED, void *call_data)
+{
+ errno = ENOSYS;
+ return 1;
+}
+
+static int
+mu_auth_radius_user_by_name (struct mu_auth_data **return_data ARG_UNUSED,
+ const void *key ARG_UNUSED,
+ void *func_data ARG_UNUSED,
+ void *call_data ARG_UNUSED)
+{
+ errno = ENOSYS;
+ return 1;
+}
+
+static int
+mu_auth_radius_user_by_uid (struct mu_auth_data **return_data,
+ const void *key,
+ void *func_data, void *call_data)
+{
+ errno = ENOSYS;
+ return 1;
+}
+#endif
+
+struct mu_auth_module mu_auth_radius_module = {
+ "radius",
+#ifdef ENABLE_RADIUS
+ &mu_radius_argp,
+#else
+ NULL,
+#endif
+ mu_radius_authenticate,
+ NULL,
+ mu_auth_radius_user_by_name,
+ NULL,
+ mu_auth_radius_user_by_uid,
+ NULL
+};
+

Return to:

Send suggestions and report system problems to the System administrator.