List:Commits« Previous MessageNext Message »
From:Georgi Kodinov Date:August 16 2010 3:42pm
Subject:bzr commit into mysql-5.5 branch (Georgi.Kodinov:3082) WL#5366
View as plain text  
#At file:///home/kgeorge/mysql/work/wl5366-5.5/ based on revid:georgi.kodinov@stripped

 3082 Georgi Kodinov	2010-08-16
      WL#5366: moved the implementation to the the new mysql-5.5 based WL1054
      tree.

    added:
      plugin/authentication_pam/
      plugin/authentication_pam/CMakeLists.txt
      plugin/authentication_pam/Makefile.am
      plugin/authentication_pam/authentication_pam.c
      plugin/authentication_pam/plug.in
      plugin/authentication_pam/safe.h
      plugin/authentication_pam/safe_getgrnam.c
      plugin/authentication_pam/safe_getpwnam.c
=== added directory 'plugin/authentication_pam'
=== added file 'plugin/authentication_pam/CMakeLists.txt'
--- a/plugin/authentication_pam/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/CMakeLists.txt	2010-08-16 15:38:01 +0000
@@ -0,0 +1,21 @@
+# Copyright (C) 2010 Sun Microsystems, Inc
+
+CHECK_INCLUDE_FILE(security/pam_appl.h HAVE_PAM_APPL_H)
+CHECK_LIBRARY_EXISTS(pam pam_start "" HAVE_PAM_START_IN_PAM)
+
+CHECK_FUNCTION_EXISTS(getpwnam_r HAVE_GETPWNAM_R)
+CHECK_FUNCTION_EXISTS(getgrnam_r HAVE_GETGRNAM_R)
+
+IF(HAVE_PAM_APPL_H)
+  SET(CMAKE_EXTRA_INCLUDE_FILES security/pam_appl.h)
+ENDIF()
+IF(HAVE_GETPWNAM_R)
+ ADD_DEFINITIONS(-DHAVE_GETPWNAM_R)
+ENDIF()
+IF(HAVE_GETGRNAM_R)
+ ADD_DEFINITIONS(-DHAVE_GETGRNAM_R)
+ENDIF()
+IF(HAVE_PAM_START_IN_PAM)
+  MYSQL_ADD_PLUGIN(authentication_pam authentication_pam.c 
+    safe_getpwnam.c safe_getgrnam.c MODULE_ONLY LINK_LIBRARIES pam)
+ENDIF()

=== added file 'plugin/authentication_pam/Makefile.am'
--- a/plugin/authentication_pam/Makefile.am	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/Makefile.am	2010-08-16 15:38:01 +0000
@@ -0,0 +1,11 @@
+pkgplugindir=$(pkglibdir)/plugin
+
+AM_LDFLAGS=-module -rpath $(pkgplugindir)
+AM_CPPFLAGS=-DMYSQL_DYNAMIC_PLUGIN -Wno-pointer-sign -I$(top_srcdir)/include
+
+if HAVE_PAM
+pkgplugin_LTLIBRARIES=  authentication_pam.la
+authentication_pam_la_SOURCES= authentication_pam.c safe_getgrnam.c safe_getpwnam.c
+endif
+
+EXTRA_DIST= CMakeLists.txt plug.in

=== added file 'plugin/authentication_pam/authentication_pam.c'
--- a/plugin/authentication_pam/authentication_pam.c	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/authentication_pam.c	2010-08-16 15:38:01 +0000
@@ -0,0 +1,765 @@
+/* Copyright (C) 2010 Oracle */
+
+/**
+  @file
+
+  PAM authentication plugin
+*/
+
+#include <my_global.h>
+#include <mysql/plugin_auth.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <security/pam_appl.h>
+
+#include "safe.h"
+
+/*#define DEBUG_PAM*/
+
+#if defined(DEBUG_PAM)
+
+#define PAM_DBUG_PRINT(a,b) \
+do { \
+  printf b; \
+  printf("\n"); \
+  fflush(stdout); \
+} while (0)
+
+#define PAM_DBUG_ENTER(x) \
+const char *__pam_auth_dbug_function= x; \
+printf("entering %s\n", x); \
+fflush (stdout)
+
+#define PAM_DBUG_RETURN(x) \
+do { \
+  printf("leaving %s on %s:%d\n", __pam_auth_dbug_function, \
+         __FILE__, __LINE__); \
+  fflush(stdout); \
+  return (x); \
+} while (0)
+
+
+#define PAM_DBUG_ASSERT(x) assert(x)
+
+
+#else /* defined(DEBUG_PAM) */
+
+#define PAM_DBUG_PRINT(a,b)
+#define PAM_DBUG_ENTER(x)
+#define PAM_DBUG_RETURN(x) return (x)
+#define PAM_DBUG_ASSERT(x)
+
+#endif /* defined(DEBUG_PAM) */
+
+/* TODO: WL2940 : replace this with calls to the server error and log */
+#define PAM_REPORT_ERROR(x) PAM_DBUG_PRINT("error", x)
+#define PAM_REPORT_MESSAGE(x) PAM_DBUG_PRINT("log", x)
+
+/********************* SERVER SIDE ****************************************/
+
+/****************** authentication string parser **************************/
+
+/**
+  Extract the next token from a string
+
+  @arg string      : the string to parse
+  @arg out next    : the position where the next token will start, NULL if none
+  @arg sep         : separator to search for
+  @eos_is_valid_separator : boolean. True if end of string is a valid separator,
+                            i.e. the separator in sep is not mandatory
+  @return    either a pointer to buffer, or an allocated demandled string, 
+             or NULL on error. Don't forget to free with free() if not buffer
+             or NULL.
+*/
+
+static char *
+auth_pam_next_token(const char *string, const char **next,
+                    char *buffer, size_t buffer_size,
+                    char sep, int eos_is_valid_separator)
+{
+  char *out, *out_buffer;
+  const char *ptr;
+  size_t out_len= 0;
+  enum { PRESPACE, IDENT, AFTERSPACE, DELIMITER, QUOTE, ERROR, DONE } state= PRESPACE;
+#if defined(DEBUG_PAM)
+  static const char *state_names[] = {
+    "PRESPACE", "IDENT", "AFTERSPACE", "DELIMITER", "QUOTE", "ERROR", "DONE"
+  };
+#endif
+
+  PAM_DBUG_ENTER("auth_pam_next_token");
+
+  out= out_buffer= buffer;
+  ptr= string;
+  PAM_DBUG_PRINT("info", ("auth_pam_next_token:reading at [%s], sep=[%c]", 
+                          ptr, sep));
+  state= PRESPACE;
+  while (state != DONE)
+  {
+    PAM_DBUG_PRINT("info",
+                   ("auth_pam_next_token:state=%s, ptr=[%s], out=[%.*s]",
+                    state_names[state], ptr, 
+                    (int) (out - out_buffer), out_buffer));
+    switch (state)
+    {
+
+    case PRESPACE:
+      while (*ptr == ' ')
+        ptr++;
+
+      if (*ptr == '"')
+        state= QUOTE;
+      else 
+        state= IDENT;
+      break;
+
+    case IDENT:
+      while (*ptr != sep && *ptr != ' ' && *ptr)
+      {
+        if (buffer_size && out_len >= buffer_size - 1)
+        {
+          /* we've depleted the buffer. Need to allocate space */
+          char *new_buffer;
+          size_t offset= out - out_buffer;
+          PAM_DBUG_PRINT("info", ("auth_pam_next_token:expanding the string"));
+          new_buffer= malloc(strlen(string) + 1);
+          if (!new_buffer)
+            PAM_DBUG_RETURN(NULL);
+          strncpy(new_buffer, out_buffer, offset);
+          out= new_buffer + (out - out_buffer);
+          out_buffer= new_buffer;
+          /* no more buffer */
+          buffer_size= 0;
+        }
+        *out++= *ptr++;
+        out_len++;
+      }
+
+      state= AFTERSPACE;
+      break;
+
+    case AFTERSPACE:
+      while (*ptr == ' ')
+        ptr++;
+
+      state= DELIMITER;
+      break;
+
+    case DELIMITER:
+      if (*ptr == sep)
+      {
+        if (next)
+          *next= ptr + 1;
+        state= DONE;
+      }
+      else if (!*ptr && eos_is_valid_separator)
+      {
+        if (next)
+          *next= NULL;
+        state= DONE;
+      }
+      else
+        state= ERROR;
+      break;
+
+    case QUOTE:
+      /*skip the quote */
+      PAM_DBUG_ASSERT(*ptr == '"');
+      ptr++;
+
+      while (*ptr)
+      {
+        if (*ptr == '\\')
+        {
+          ptr++;
+          if (!*ptr)
+          {
+            state= ERROR;
+            break;
+          }
+        }
+        else if (*ptr == '"')
+          break;
+        if (buffer_size && out_len >= buffer_size - 1)
+        {
+          /* we've depleted the buffer. Need to allocate space */
+          char *new_buffer;
+          size_t offset= out - out_buffer;
+          PAM_DBUG_PRINT("info", ("auth_pam_next_token:expanding the string"));
+          new_buffer= malloc(strlen(string) + 1);
+          if (!new_buffer)
+            PAM_DBUG_RETURN(NULL);
+          strncpy(new_buffer, out_buffer, offset);
+          out= new_buffer + (out - out_buffer);
+          out_buffer= new_buffer;
+          /* no more buffer */
+          buffer_size= 0;
+        }
+        *out++= *ptr++;
+        out_len++;
+      }
+
+      if (*ptr != '"')
+      {
+        state= ERROR;
+        break;
+      }
+
+      ptr++;
+
+      state= AFTERSPACE;
+      break;
+
+    case ERROR:
+      free(out_buffer);
+      PAM_DBUG_RETURN(NULL);
+
+    case DONE:
+      /* should never be reached */
+      break;
+    }
+  }
+
+  *out= 0;
+  PAM_DBUG_PRINT("info",
+                 ("auth_pam_next_token:state=%s, ptr=[%s], out=[%s]",
+                  state_names[state], ptr, out_buffer));
+  PAM_DBUG_RETURN(out_buffer);
+}
+
+
+/**
+  Walk through the comma separated list of name/value pairs and call a handler
+
+  @param out authenticated_as       an output buffer to pass to the map function.
+  @param in  size_authenticated_as  length of authenticated_as
+  @param in  pam_user               the user data to pass to the map function
+  @param in  group_mapping          the comma separated list of name=value pairs
+  @param in  map_group_to_user      the mapping function to check and substitute
+                                      the mysql user name if the current group is
+                                      found in the groups assigned to the user
+  @param out substitution_was_made  set to non-zero when a substitution was made.
+
+  @return status
+  @retval 0 success
+  @retval 1 error
+*/
+
+
+static int
+walk_namevalue_list(char *authenticated_as, size_t size_of_authenticated_as,
+                    void *pam_user, const char *group_mapping,
+                    int (*map_group_to_user) (char *auth_as, size_t size,
+                                              void *pam_user,
+                                              const char *name, 
+                                              const char *value,
+                                              int *substitution_was_made),
+                    int *substitution_was_made)
+{
+  const char *current_map= group_mapping;
+  char name_buffer[64], group_buffer[64];
+  PAM_DBUG_ENTER("walk_namevalue_list");
+
+  *substitution_was_made= 0;
+
+  while (current_map)
+  {
+    const char *next;
+    char *group, *mysql_name;
+
+    PAM_DBUG_PRINT ("info", ("walk_namevalue_list:reading at: [%s]",
+                             current_map));
+    if (!(group= auth_pam_next_token(current_map, &next, 
+                                     group_buffer, sizeof (group_buffer), 
+                                     '=', 0)))
+      PAM_DBUG_RETURN(1);
+    PAM_DBUG_PRINT("info", ("walk_namevalue_list:name=[%s]", group));
+
+    if (!next)
+    {
+      if (group != group_buffer)
+        free(group);
+      PAM_DBUG_RETURN(1);
+    }
+
+    if (!(mysql_name= auth_pam_next_token(next, &current_map, 
+                                          name_buffer, sizeof(name_buffer),
+                                          ',', 1)))
+    {
+      if (group != group_buffer)
+        free(group);
+      PAM_DBUG_RETURN(1);
+    }
+    PAM_DBUG_PRINT ("info", ("walk, &error_namevalue_list:value=[%s]", mysql_name));
+
+    if (map_group_to_user(authenticated_as, size_of_authenticated_as,
+                          pam_user, group, mysql_name, substitution_was_made))
+    {
+      PAM_DBUG_PRINT ("info", ("walk_namevalue_list:error finding mapping"));
+      if (group != group_buffer)
+        free(group);
+      if (mysql_name != name_buffer)
+        free(mysql_name);
+      PAM_DBUG_RETURN(1);
+    }
+
+    if (group != group_buffer)
+      free(group);
+    if (mysql_name != name_buffer)
+      free(mysql_name);
+
+    if (*substitution_was_made)
+    {
+      PAM_DBUG_PRINT ("info", ("walk_namevalue_list:found mapping"));
+      PAM_DBUG_RETURN(0);
+    }
+  }
+
+  PAM_DBUG_RETURN(0);
+}
+
+
+/***************** os_group=mysql_user list handling *************************/
+
+/**
+  Walk through groups assigned to a user and substitute the mysql name if a match
+  is found.
+
+  @param out auth_as                an output buffer for the resulting user name.
+  @param in  size_auth_as           length of authenticated_as
+  @param in  pam_user               info about the current user
+  @param in  name                   the group name
+  @param in  name_len               length of name
+  @param in  value                  the mysql user name to use
+  @param in  value_len              length of value
+  @param out substitution_was_made  set to non-zero when a substitution was made.
+
+  @return status
+  @retval 0 success
+  @retval 1 error
+*/
+
+static int 
+map_grop_to_user(char *auth_as, size_t size_auth_as, void *pam_user, 
+                 const char *name, const char *value, 
+                 int *substitution_was_made)
+{
+  struct passwd *pwd= (struct passwd *) pam_user;
+  struct group grp_buf, *grp;
+  char buf[512], *to_free= NULL;
+  char **members;
+
+  PAM_DBUG_ENTER("map_grop_to_user");
+  PAM_DBUG_ASSERT(size_auth_as > 0);
+
+  PAM_DBUG_PRINT("info", ("map_grop_to_user:pam_user=%s, name=%s, value=%s",
+                 pwd->pw_name, name, value));
+
+  grp= auth_pam_safe_getgrnam_r(name, buf, sizeof(buf), &grp_buf, &to_free);
+  if (!grp)
+  {
+    if (to_free)
+      free (to_free);
+    *substitution_was_made= 0;
+    /* return success here to let the caller to continue searching */
+    PAM_DBUG_RETURN(0);
+  }
+
+  *substitution_was_made= 1;
+
+  if (grp->gr_gid == pwd->pw_gid)
+  {
+    PAM_DBUG_PRINT("info", ("map_grop_to_user:primary group match"));
+    goto found;
+  }
+
+  members= grp->gr_mem;
+  while (*members)
+  {
+    PAM_DBUG_PRINT("info", ("examining member %s", *members));
+    if (!strcmp(*members, name))
+      goto found;
+    members++;
+  }
+  
+  *substitution_was_made= 0;
+
+found:
+  if (*substitution_was_made)
+  {
+    strncpy(auth_as, value, size_auth_as - 1);
+    auth_as[size_auth_as - 1]= 0;
+    PAM_DBUG_PRINT("info", ("substitution was made to mysql user %s", auth_as));
+  }
+  if (to_free)
+    free(to_free);
+  PAM_DBUG_RETURN(0);
+}
+
+
+/*
+ 
+ Top level parsing function for the OS_group=mysql_user comma separated list
+ 
+ Retrieves the user information from the OS and parses the list to find
+ matching groups.
+
+  @param out authenticated_as       an output buffer to pass to the map function.
+  @param in  size_authenticated_as  length of authenticated_as
+  @param in  pam_user               string : user name of the logged in user
+  @param in  group_mapping          the comma separated list of name=value pairs
+  @param out substitution_was_made  set to non-zero when a substitution was made.
+
+  @return status
+  @retval 0 success
+  @retval 1 error
+*/
+
+static int
+auth_pam_map_groups(char *authenticated_as, size_t size_of_authenticated_as,
+                    char *pam_user, const char *group_mapping,
+                    int *substitution_was_made)
+{
+  struct passwd pwd_buf, *pwd;
+  char start_buf[512];
+  char *to_free= NULL;
+  int rc;
+
+  PAM_DBUG_ENTER("auth_pam_map_groups");
+
+  pwd= auth_pam_safe_getpwnam_r(pam_user, start_buf, sizeof(start_buf), 
+                                &pwd_buf, &to_free);
+  if (!pwd)
+  {
+    if (to_free)
+      free (to_free);
+    *substitution_was_made= 0;
+    /* return success here to let the caller to continue searching */
+    PAM_DBUG_RETURN(0);
+  }
+
+  rc= walk_namevalue_list(authenticated_as, size_of_authenticated_as, 
+                          pwd, group_mapping, map_grop_to_user, 
+                          substitution_was_made);
+
+  PAM_DBUG_PRINT("info", ("walk_namevalue_list returned %d", rc));
+
+  if (to_free)
+    free(to_free);
+
+  PAM_DBUG_RETURN(rc);
+}
+
+
+/*********************** plugin proper *******************************/
+
+/**
+  PAM server dialog function
+
+  See man 3 pam_conv for details on arguments and return values.
+  Expects a password null-terminated string passed as appdata_ptr
+*/
+
+static int
+auth_pam_server_conv(int num_msg, const struct pam_message **msg,
+                     struct pam_response **resp, void *appdata_ptr)
+{
+  int i;
+  const struct pam_message *m= *msg;
+  struct pam_response *r, *reply;
+  char *password= (char *) appdata_ptr;
+
+  PAM_DBUG_ENTER ("auth_pam_server_conv");
+
+  /* Allocate space for the replies */
+  if ((reply= r= calloc(num_msg, sizeof(struct pam_response))) == NULL)
+  {
+    PAM_REPORT_ERROR(("Out of memory"));
+    PAM_DBUG_RETURN(PAM_CONV_ERR);
+  }
+
+  /* Loop through requests */
+  for (i= 0; i < num_msg; i++, m++, r++)
+  {
+    switch (m->msg_style)
+    { 
+      case PAM_PROMPT_ECHO_OFF:
+        /* This ought to be a password request. Send the password back */
+        PAM_DBUG_PRINT("info", ("auth_pam_server_conv:"
+                                "PAM_PROMPT_ECHO_OFF [%s] received",
+                                m->msg ? m->msg : NULL));
+        r->resp= strdup(password);
+        if (!r->resp)
+        {
+          PAM_REPORT_ERROR(("Out of memory"));
+          goto error;
+        }
+        break;
+
+      case PAM_TEXT_INFO:
+        /* PAM decided to inform us about something. Send back to the server */
+        PAM_REPORT_MESSAGE(("PAM information received : %s", 
+                            m->msg ? m->msg : ""));
+        break;
+
+      /* Can't handle the below properly */
+
+      case PAM_PROMPT_ECHO_ON:
+        /*
+          PAM asks us about something that's not a password. Bail out with
+          error. 
+        */
+        PAM_REPORT_ERROR(("Not implemented ECHO_ON question received : %s", 
+                          m->msg ? m->msg : NULL));
+        goto error;
+
+      case PAM_ERROR_MSG:
+        /* PAM has sent an error message to us. Send back to the server */
+        PAM_REPORT_ERROR(("PAM error received : %s", m->msg ? m->msg : NULL));
+        goto error;
+
+      default:
+        /* PAM has sent a question we don't know about. Bail out */
+        PAM_REPORT_ERROR(("Not implemented %d question received : %s", 
+                          m->msg_style, m->msg ? m->msg : NULL));
+        goto error;
+    };
+    /* Provably not needed. Set for completeness */
+    r->resp_retcode= PAM_SUCCESS;
+  }
+
+  /* 
+    All is well. set the set of replies back to PAM. 
+    Note: PAM will free this structire. 
+  */
+  *resp= reply;
+  PAM_DBUG_RETURN(PAM_SUCCESS);
+
+error:
+  for (i= 0, r= reply; i < num_msg; i++, r++)
+    if (r->resp)
+      free (r->resp);
+  free(reply);
+
+  PAM_DBUG_RETURN(PAM_CONV_ERR);
+}
+
+
+/**
+  PAM authentication plugin
+*/
+
+static int auth_pam_server(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+  const char *group_mapping;
+  pam_handle_t *pamh = NULL;
+  struct pam_conv conv;  
+  int rc, pkt_len, substitution_was_made= 0;
+  unsigned char *pkt;
+  char *password= NULL;
+  const void *pam_user;
+  char *pam_service;
+  char pam_service_buffer[64];
+
+  PAM_DBUG_ENTER("auth_pam_server");
+
+  /* extract the service name */
+  if (!(pam_service= auth_pam_next_token(info->auth_string, &group_mapping,
+                                         pam_service_buffer, 
+                                         sizeof(pam_service_buffer), 
+                                         ',', 1)))
+  {
+    PAM_REPORT_ERROR(("PAM plugin error: out of memory"));
+    PAM_DBUG_RETURN(CR_ERROR);
+  }
+
+  /* read the clear text password from the client */
+  if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+  {
+    if (pam_service != pam_service_buffer)
+      free(pam_service);
+    PAM_REPORT_ERROR(("PAM plugin error: error reading password from client"));
+    PAM_DBUG_RETURN(CR_ERROR);
+  }
+
+
+  /* store it in an allocated buffer as null terminated string */
+  password= malloc(pkt_len + 1);
+  if (!password)
+  {
+    if (pam_service != pam_service_buffer)
+      free(pam_service);
+    PAM_REPORT_ERROR(("PAM plugin error: out of memory"));
+    PAM_DBUG_RETURN(CR_ERROR);
+  }
+  memcpy(password, pkt, pkt_len);
+  password[pkt_len]= 0;
+  PAM_DBUG_PRINT("info", ("auth_pam_server:password %s received", password));
+
+  /* initialize the pam_conv structure */
+  conv.conv= auth_pam_server_conv;
+  conv.appdata_ptr= password;
+
+  /* obtain a PAM handle */
+  rc= pam_start(pam_service, info->user_name, &conv, &pamh);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_start rc=%d", rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* pass the requesting user name to PAM */
+  rc= pam_set_item(pamh, PAM_RUSER, info->user_name);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_set_item(PAM_RUSER,%s) rc=%d", 
+                      info->user_name, rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* pass the requesting host name to PAM */
+  rc= pam_set_item(pamh, PAM_RHOST, info->host_or_ip);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_set_item(PAM_RHOST,%s) rc=%d", 
+                      info->host_or_ip, rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* ask PAM if this user has supplied valid credentials */
+  rc= pam_authenticate(pamh, PAM_SILENT);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_authenticate rc=%d", rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* ask PAM if this user can login */
+  rc= pam_acct_mgmt(pamh, PAM_SILENT);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_acct_mgmt rc=%d", rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* tell PAM to complete the login process */
+  rc= pam_setcred(pamh, PAM_ESTABLISH_CRED);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_setcred rc=%d", rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* get the logged in user name from PAM */
+  rc= pam_get_item(pamh, PAM_USER, &pam_user);
+  PAM_DBUG_PRINT("info", ("auth_pam_server:pam_get_item rc=%d", rc));
+  if (rc != PAM_SUCCESS)
+    goto report_error;
+
+  /* try to parse the group mapping if present */
+  if (group_mapping && auth_pam_map_groups(info->authenticated_as, 
+                                           sizeof(info->authenticated_as),
+                                           (char *) pam_user, group_mapping, 
+                                           &substitution_was_made))
+  {
+    rc= PAM_SYSTEM_ERR;
+    goto report_error;
+  }
+
+  /* and return it to the server */
+  if (!substitution_was_made)
+    strncpy(info->authenticated_as, (char *) pam_user, 
+            sizeof (info->authenticated_as));
+
+  PAM_DBUG_PRINT("info", ("auth_pam_server:authenticated_as=%s", 
+                      info->authenticated_as));
+
+  if (password && *password)
+    info->password_used= 1;
+
+  /* All passed well. Tell the server */
+  rc= PAM_SUCCESS;
+
+report_error:
+  PAM_DBUG_PRINT("info", ("auth_pam_server: rc=%d", rc));
+  if (rc != PAM_SUCCESS)
+  {
+    /* report error */;
+    const char *err;
+    if (pamh)
+      err= pam_strerror(pamh, rc);
+    else
+      err= "Unspecified PAM plugin error";
+
+    PAM_REPORT_ERROR(("PAM error: %s", err));
+  }
+
+  if (pam_service != pam_service_buffer)
+    free(pam_service);
+
+  if (pamh)
+  {
+    pam_end(pamh, rc);
+    pamh= NULL;
+  }
+
+  if (password)
+  {
+    free(password);
+    password= NULL;
+  }
+
+  PAM_DBUG_RETURN(rc == PAM_SUCCESS ? CR_OK : CR_ERROR);
+}
+
+
+static struct st_mysql_auth auth_pam_handler=
+{
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  "authentication_pam", /* requires auth_pam_plugin client's plugin */
+  auth_pam_server
+};
+
+mysql_declare_plugin(auth_pam)
+{
+  MYSQL_AUTHENTICATION_PLUGIN,
+  &auth_pam_handler,
+  "authentication_pam",
+  "Georgi Kodinov",
+  "PAM authentication plugin",
+  PLUGIN_LICENSE_GPL,
+  NULL,
+  NULL,
+  0x0100,
+  NULL,
+  NULL,
+  NULL
+}
+mysql_declare_plugin_end;
+
+/********************* CLIENT SIDE ***************************************/
+/*
+  client PAM authentication plugin 
+*/
+#include <mysql.h>
+
+/**
+  The main function of the PAM authentication plugin.
+*/
+
+static int auth_pam_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+  int res;
+
+  /* send password in clear text */
+  res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, 
+						 strlen(mysql->passwd) + 1);
+
+  return res ? CR_ERROR : CR_OK;
+}
+
+
+mysql_declare_client_plugin(AUTHENTICATION)
+  "authentication_pam",
+  "Georgi Kodinov",
+  "PAM Authentication Plugin",
+  {0,1,0},
+  NULL,
+  NULL,
+  auth_pam_client
+mysql_end_client_plugin;

=== added file 'plugin/authentication_pam/plug.in'
--- a/plugin/authentication_pam/plug.in	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/plug.in	2010-08-16 15:38:01 +0000
@@ -0,0 +1,19 @@
+MYSQL_PLUGIN(authentication_pam, [PAM authentication plugin],
+                   [Plugin to authenticate MySQL users using PAM])
+MYSQL_PLUGIN_DYNAMIC(authentication_pam, [authentication_pam.la])
+
+AC_CHECK_LIB(pam, pam_start)
+AC_CHECK_HEADER(security/pam_appl.h,
+                [AC_DEFINE([HAVE_PAM_APPL_H],[1],
+                           [Define to 1 if you have <security/pam_appl.h>.])],[])
+AC_CHECK_FUNCS(getpwnam_r getgrnam_r)
+
+AC_COMPILE_IFELSE([
+  AC_LANG_PROGRAM([[
+            #include <security/pam_appl.h>
+]],[
+            pam_handle_t *pamh;
+            struct pam_conv conv;
+            pam_start("mysql", "user", &conv, &pamh);
+])],have_pam=yes)
+AM_CONDITIONAL(HAVE_PAM, test x$have_pam = xyes)

=== added file 'plugin/authentication_pam/safe.h'
--- a/plugin/authentication_pam/safe.h	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/safe.h	2010-08-16 15:38:01 +0000
@@ -0,0 +1,9 @@
+/* Copyright (C) 2010 Oracle */
+
+struct group *auth_pam_safe_getgrnam_r(const char *name, char *buf_arg,
+                                       size_t buf_size, struct group *g_arg,
+                                       char **to_free);
+struct passwd *auth_pam_safe_getpwnam_r(const char *name, char *buf_arg,
+                                        size_t buf_size,
+                                        struct passwd *pwd_arg, 
+                                        char **to_free);

=== added file 'plugin/authentication_pam/safe_getgrnam.c'
--- a/plugin/authentication_pam/safe_getgrnam.c	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/safe_getgrnam.c	2010-08-16 15:38:01 +0000
@@ -0,0 +1,74 @@
+/* Copyright (C) 2010 Oracle */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <grp.h>
+#include <config.h>
+#include "safe.h"
+
+struct group *
+auth_pam_safe_getgrnam_r(const char *name, char *buf_arg, size_t buf_size,
+                         struct group *g_arg, char **to_free)
+{
+  *to_free= NULL;
+#if defined(__APPLE__)
+  /* Apple is known to provide a thread safe getpwnam() function */
+  return getgrnam(name);
+#elif defined(HAVE_GETGRNAM_R)
+  char *buf= buf_arg;
+  int rc= 0;
+  struct group *ret= NULL;
+
+  if (!buf)
+  {
+    if (!buf_size)
+      buf_size= 512;
+    *to_free= buf= (char *) malloc(buf_size);
+    if (!buf)
+      return NULL;
+  }
+
+  if (!buf_size)
+    return NULL;
+
+  do
+  {
+    rc= getgrnam_r(name, g_arg, buf, buf_size, &ret);
+
+    if (rc == ERANGE)
+    {
+      /*
+        The buffer specified by the buffer and bufsize argu-
+        ments was insufficiently sized to store the result.
+        The caller should retry with a larger buffer.
+      */
+      buf= (char *) ((*to_free) ?
+        realloc(*to_free, buf_size * 2) :
+        malloc(buf_size * 2));
+
+      if (!buf)
+      {
+        free(*to_free);
+        *to_free= NULL;
+        return NULL;
+      }
+
+      *to_free= buf;
+      buf_size*= 2;
+      continue;
+    }
+  } while (0);
+
+  if (rc)
+  {
+    free(to_free);
+    *to_free= NULL;
+    return NULL;
+  }
+
+  return ret;
+#else
+  return NULL;
+#endif
+}

=== added file 'plugin/authentication_pam/safe_getpwnam.c'
--- a/plugin/authentication_pam/safe_getpwnam.c	1970-01-01 00:00:00 +0000
+++ b/plugin/authentication_pam/safe_getpwnam.c	2010-08-16 15:38:01 +0000
@@ -0,0 +1,77 @@
+/* Copyright (C) 2010 Oracle */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <pwd.h>
+#include <config.h>
+#include "safe.h"
+
+struct passwd *
+auth_pam_safe_getpwnam_r(const char *name, char *buf_arg, size_t buf_size,
+                         struct passwd *pwd_arg, char **to_free)
+{
+  *to_free= NULL;
+#if defined(__APPLE__)
+  /* Apple is known to provide a thread safe getpwnam() function */
+  return getpwnam(name);
+#elif defined(HAVE_GETPWNAM_R)
+  char *buf= buf_arg;
+  int rc= 0;
+  struct passwd *ret= NULL;
+
+  if (!buf)
+  {
+    if (!buf_size)
+      buf_size= 512;
+    *to_free= buf= (char *) malloc(buf_size);
+    if (!buf)
+      return NULL;
+  }
+
+  if (!buf_size)
+    return NULL;
+
+  do
+  {
+    rc= getpwnam_r(name, pwd_arg, buf, buf_size, &ret);
+
+    if (rc == ERANGE)
+    {
+      /*
+        The buffer specified by the buffer and bufsize argu-
+        ments was insufficiently sized to store the result.
+        The caller should retry with a larger buffer.
+      */
+      buf= (char *) ((*to_free) ? 
+        realloc(*to_free, buf_size * 2) :
+        malloc(buf_size * 2));
+
+      if (!buf)
+      {
+        free(*to_free);
+        *to_free= NULL;
+        return NULL;
+      }
+
+      *to_free= buf;
+      buf_size*= 2;
+      continue;
+    }
+  } while (0);
+
+  if (rc)
+  {
+    free(to_free);
+    *to_free= NULL;
+    return NULL;
+  }
+
+  return ret;
+#else
+  return NULL;
+#endif
+}
+
+


Attachment: [text/bzr-bundle] bzr/georgi.kodinov@oracle.com-20100816153801-jdr8ikpa5knf35rn.bundle
Thread
bzr commit into mysql-5.5 branch (Georgi.Kodinov:3082) WL#5366Georgi Kodinov16 Aug
  • Re: bzr commit into mysql-5.5 branch (Georgi.Kodinov:3082) WL#5366Rafal Somla10 Sep
  • Re: bzr commit into mysql-5.5 branch (Georgi.Kodinov:3082) WL#5366Rafal Somla10 Sep