1 /* auth_pam.c
2  *
3  * Shamelessly slightly rewritten pamize.h from FreeBSD
4  */
5 /* $FreeBSD: src/libexec/lukemftpd/pamize.h,v 1.1 2003/02/02 21:06:10 obrien Exp $ */
6 
7 #ifndef lint
8 static const char * rcsid = "@(#) $Id$";
9 #endif
10 
11 #include <config.h>
12 
13 #include "conf.h"
14 
15 #ifdef WITH_PAM
16 /*
17  * the following code is stolen from imap-uw PAM authentication module and
18  * login.c
19  */
20 
21 static int
auth_conv(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata)22 auth_conv(int num_msg, const struct pam_message **msg,
23       struct pam_response **resp, void *appdata)
24 {
25     int i;
26     cred_t *cred = (cred_t *) appdata;
27     struct pam_response *reply;
28 
29     reply = calloc(num_msg, sizeof *reply);
30     if (reply == NULL)
31         return PAM_BUF_ERR;
32 
33     for (i = 0; i < num_msg; i++) {
34         switch (msg[i]->msg_style) {
35         case PAM_PROMPT_ECHO_ON:    /* assume want user name */
36             reply[i].resp_retcode = PAM_SUCCESS;
37             reply[i].resp = COPY_STRING(cred->uname);
38             /* PAM frees resp. */
39             break;
40         case PAM_PROMPT_ECHO_OFF:   /* assume want password */
41             reply[i].resp_retcode = PAM_SUCCESS;
42             reply[i].resp = COPY_STRING(cred->pass);
43             /* PAM frees resp. */
44             break;
45         case PAM_TEXT_INFO:
46         case PAM_ERROR_MSG:
47             reply[i].resp_retcode = PAM_SUCCESS;
48             reply[i].resp = NULL;
49             break;
50         default:            /* unknown message style */
51             free(reply);
52             return PAM_CONV_ERR;
53         }
54     }
55 
56     *resp = reply;
57     return PAM_SUCCESS;
58 }
59 
60 /*
61  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
62  * authenticated, or 1 if not authenticated.  If some sort of PAM system
63  * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
64  * function returns -1.  This can be used as an indication that we should
65  * fall back to a different authentication mechanism.
66  */
67 int
auth_pam(struct passwd ** ppw,const char * pass)68 auth_pam(struct passwd **ppw, const char *pass)
69 {
70     const char *tmpl_user;
71     const void *item;
72     int rval;
73     int e;
74     cred_t auth_cred = { (*ppw)->pw_name, pass };
75     struct pam_conv conv = { &auth_conv, &auth_cred };
76 
77     e = pam_start("calife", (*ppw)->pw_name, &conv, &pamh);
78     if (e != PAM_SUCCESS) {
79         syslog(LOG_AUTH | LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
80         return -1;
81     }
82     MESSAGE_1("pam_start succeeded for %s\n", (*ppw)->pw_name);
83 
84     /* pam_authenticate may require root privs to obtain shadow
85        password data */
86     GET_ROOT;
87     e = pam_authenticate(pamh, 0);
88     RELEASE_ROOT;
89     switch (e) {
90     case PAM_SUCCESS:
91         /*
92          * With PAM we support the concept of a "template"
93          * user.  The user enters a login name which is
94          * authenticated by PAM, usually via a remote service
95          * such as RADIUS or TACACS+.  If authentication
96          * succeeds, a different but related "template" name
97          * is used for setting the credentials, shell, and
98          * home directory.  The name the user enters need only
99          * exist on the remote authentication server, but the
100          * template name must be present in the local password
101          * database.
102          *
103          * This is supported by two various mechanisms in the
104          * individual modules.  However, from the application's
105          * point of view, the template user is always passed
106          * back as a changed value of the PAM_USER item.
107          */
108         if ((e = pam_get_item(pamh, PAM_USER, &item)) == PAM_SUCCESS)
109         {
110             tmpl_user = (const char *) item;
111             if (strcmp((*ppw)->pw_name, tmpl_user) != 0)
112                 *ppw = getpwnam(tmpl_user);
113         } else
114             syslog(LOG_AUTH | LOG_ERR, "Couldn't get PAM_USER: %s",
115                                        pam_strerror(pamh, e));
116         rval = 0;
117         break;
118 
119     case PAM_AUTH_ERR:
120     case PAM_USER_UNKNOWN:
121     case PAM_MAXTRIES:
122         syslog(LOG_AUTH | LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e));
123         rval = 1;
124         break;
125 
126     default:
127         syslog(LOG_AUTH | LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh,\
128                                                                         e));
129         rval = -1;
130         break;
131     }
132 
133 /* XXX this does not work on solaris10 */
134 #ifndef solaris
135     if (rval == 0) {
136         e = pam_acct_mgmt(pamh, 0);
137         if (e == PAM_NEW_AUTHTOK_REQD) {
138             e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
139             if (e != PAM_SUCCESS) {
140                 syslog(LOG_AUTH | LOG_ERR, "pam_chauthtok: %s",
141                     pam_strerror(pamh, e));
142                 rval = 1;
143             }
144         } else if (e != PAM_SUCCESS) {
145             rval = 1;
146         }
147     }
148 #endif /* solaris */
149 
150     MESSAGE_1 ("auth_pam returns %d\n", rval);
151     return rval;
152 }
153 
154 #endif /* WITH_PAM */
155