1/* MODULE: auth_pam */
2
3/* COPYRIGHT
4 * Copyright (c) 2000 Fabian Knittel.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain any existing copyright
11 *    notice, and this entire permission notice in its entirety,
12 *    including the disclaimer of warranties.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in
16 *    the documentation and/or other materials provided with the
17 *    distribution.
18 *
19 * 2. Redistributions in binary form must reproduce all prior and current
20 *    copyright notices, this list of conditions, and the following
21 *    disclaimer in the documentation and/or other materials provided
22 *    with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
30 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
32 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
33 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
34 * DAMAGE.
35 * END COPYRIGHT */
36
37/*
38 * Pluggable Authentication Modules, PAM(8), based authentication module
39 * for saslauthd.
40 *
41 * Written by Fabian Knittel <fknittel@gmx.de>. Original implementation
42 * Debian's pwcheck_pam daemon by Michael-John Turner <mj@debian.org>.
43 */
44
45/* PUBLIC DEPENDENCIES */
46#include "mechanisms.h"
47#include <stdio.h>
48
49#ifdef HAVE_CONFIG_H
50#include <config.h>
51#endif
52
53#ifdef AUTH_PAM
54
55# include <string.h>
56# include <syslog.h>
57#ifdef HAVE_SECURITY_PAM_APPL_H
58# include <security/pam_appl.h>
59#elif defined(HAVE_PAM_PAM_APPL_H)
60# include <pam/pam_appl.h>
61#endif
62
63# include "auth_pam.h"
64/* END PUBLIC DEPENDENCIES */
65
66
67/* Structure for application specific data passed through PAM
68 * to our conv call-back routine saslauthd_pam_conv. */
69typedef struct {
70    const char *login;			/* plaintext authenticator */
71    const char *password;		/* plaintext password */
72    pam_handle_t *pamh;			/* pointer to PAM handle */
73} pam_appdata;
74
75# define RETURN(x) return strdup(x)
76
77
78/* FUNCTION: saslauthd_pam_conv */
79
80/* SYNOPSIS
81 * Call-back function used by the PAM library to communicate with us. Each
82 * received message expects a response, pointed to by resp.
83 * END SYNOPSIS */
84
85static int				/* R: PAM return code */
86saslauthd_pam_conv (
87  /* PARAMETERS */
88  int num_msg,				/* I: number of messages */
89  const struct pam_message **msg,	/* I: pointer to array of messages */
90  struct pam_response **resp,		/* O: pointer to pointer of response */
91  void *appdata_ptr			/* I: pointer to app specific data */
92  /* END PARAMETERS */
93  )
94{
95    /* VARIABLES */
96    pam_appdata *my_appdata;		/* application specific data */
97    struct pam_response *my_resp;	/* response created by this func */
98    int i;				/* loop counter */
99    const char *login_prompt;		/* string prompting for user-name */
100    int rc;				/* return code holder */
101    /* END VARIABLES */
102
103    my_appdata = appdata_ptr;
104
105    my_resp = malloc(sizeof(struct pam_response) * num_msg);
106    if (my_resp == NULL)
107	return PAM_CONV_ERR;
108
109    for (i = 0; i < num_msg; i++)
110	switch (msg[i]->msg_style) {
111	/*
112	 * We assume PAM_PROMPT_ECHO_OFF to be a request for password.
113	 * This assumption might be unsafe.
114	 *
115	 * For PAM_PROMPT_ECHO_ON we first check whether the provided
116	 * request string matches PAM_USER_PROMPT and, only if they do
117	 * match, assume it to be a request for the login.
118	 */
119	case PAM_PROMPT_ECHO_OFF:	/* password */
120	    my_resp[i].resp = strdup(my_appdata->password);
121	    if (my_resp[i].resp == NULL) {
122		syslog(LOG_DEBUG, "DEBUG: saslauthd_pam_conv: strdup failed");
123		goto ret_error;
124	    }
125	    my_resp[i].resp_retcode = PAM_SUCCESS;
126	    break;
127
128	case PAM_PROMPT_ECHO_ON:	/* username? */
129	    /* Recheck setting each time, as it might have been changed
130	       in the mean-while. */
131	    rc = pam_get_item(my_appdata->pamh, PAM_USER_PROMPT,
132			      (void *) &login_prompt);
133	    if (rc != PAM_SUCCESS) {
134		syslog(LOG_DEBUG, "DEBUG: saslauthd_pam_conv: unable to read "
135		       "login prompt string: %s",
136		       pam_strerror(my_appdata->pamh, rc));
137		goto ret_error;
138	    }
139
140	    if (strcmp(msg[i]->msg, login_prompt) == 0) {
141		my_resp[i].resp = strdup(my_appdata->login);
142		my_resp[i].resp_retcode = PAM_SUCCESS;
143	    } else {			/* ignore */
144		syslog(LOG_DEBUG, "DEBUG: saslauthd_pam_conv: unknown prompt "
145		       "string: %s", msg[i]->msg);
146		my_resp[i].resp = NULL;
147		my_resp[i].resp_retcode = PAM_SUCCESS;
148	    }
149	    break;
150
151	case PAM_ERROR_MSG:		/* ignore */
152	case PAM_TEXT_INFO:		/* ignore */
153	    my_resp[i].resp = NULL;
154	    my_resp[i].resp_retcode = PAM_SUCCESS;
155	    break;
156
157	default:			/* error */
158	    goto ret_error;
159	}
160    *resp = my_resp;
161    return PAM_SUCCESS;
162
163ret_error:
164    /*
165     * Free response structure. Don't free my_resp[i], as that
166     * isn't initialised yet.
167     */
168    {
169	int y;
170
171	for (y = 0; y < i; y++)
172	    if (my_resp[y].resp != NULL)
173		free(my_resp[y].resp);
174	free(my_resp);
175    }
176    return PAM_CONV_ERR;
177}
178
179/* END FUNCTION: saslauthd_pam_conv */
180
181/* FUNCTION: auth_pam */
182
183char *					/* R: allocated response string */
184auth_pam (
185  /* PARAMETERS */
186  const char *login,			/* I: plaintext authenticator */
187  const char *password,			/* I: plaintext password */
188  const char *service,			/* I: service name */
189  const char *realm __attribute__((unused))
190  /* END PARAMETERS */
191  )
192{
193    /* VARIABLES */
194    pam_appdata my_appdata;		/* application specific data */
195    struct pam_conv my_conv;		/* pam conversion data */
196    pam_handle_t *pamh;			/* pointer to PAM handle */
197    int rc;				/* return code holder */
198    /* END VARIABLES */
199
200    my_appdata.login = login;
201    my_appdata.password = password;
202    my_appdata.pamh = NULL;
203
204    my_conv.conv = saslauthd_pam_conv;
205    my_conv.appdata_ptr = &my_appdata;
206
207    rc = pam_start(service, login, &my_conv, &pamh);
208    if (rc != PAM_SUCCESS) {
209	syslog(LOG_DEBUG, "DEBUG: auth_pam: pam_start failed: %s",
210	       pam_strerror(pamh, rc));
211	RETURN("NO PAM start error");
212    }
213
214    my_appdata.pamh = pamh;
215
216    rc = pam_authenticate(pamh, PAM_SILENT);
217    if (rc != PAM_SUCCESS) {
218	syslog(LOG_DEBUG, "DEBUG: auth_pam: pam_authenticate failed: %s",
219	       pam_strerror(pamh, rc));
220	pam_end(pamh, rc);
221	RETURN("NO PAM auth error");
222    }
223
224    rc = pam_acct_mgmt(pamh, PAM_SILENT);
225    if (rc != PAM_SUCCESS) {
226	syslog(LOG_DEBUG, "DEBUG: auth_pam: pam_acct_mgmt failed: %s",
227	       pam_strerror(pamh, rc));
228	pam_end(pamh, rc);
229	RETURN("NO PAM acct error");
230    }
231
232    pam_end(pamh, PAM_SUCCESS);
233    RETURN("OK");
234}
235
236/* END FUNCTION: auth_pam */
237
238#else /* !AUTH_PAM */
239
240char *
241auth_pam(
242  const char *login __attribute__((unused)),
243  const char *password __attribute__((unused)),
244  const char *service __attribute__((unused)),
245  const char *realm __attribute__((unused))
246  )
247{
248    return NULL;
249}
250
251#endif /* !AUTH_PAM */
252
253/* END MODULE: auth_pam */
254