GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / saslauthd / auth_pam.c
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. */
69 typedef 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 \f
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
85 static int                              /* R: PAM return code */
86 saslauthd_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
163 ret_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 \f
181 /* FUNCTION: auth_pam */
182
183 char *                                  /* R: allocated response string */
184 auth_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
240 char *
241 auth_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 */