rationnalize includes a lot:
[apps/madmutt.git] / imap / auth_gss.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1999-2000 Brendan Cully <brendan@kublai.com>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 /* GSS login/authentication code */
11
12 #include <lib-lib/lib-lib.h>
13
14 #ifdef USE_GSS
15
16 #include <netinet/in.h>
17
18 #ifdef HAVE_HEIMDAL
19 #  include <gssapi/gssapi.h>
20 #  define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
21 #else
22 #  include <gssapi/gssapi.h>
23 #  include <gssapi/gssapi_generic.h>
24 #endif
25
26 #define GSS_BUFSIZE 8192
27
28 #define GSS_AUTH_P_NONE      1
29 #define GSS_AUTH_P_INTEGRITY 2
30 #define GSS_AUTH_P_PRIVACY   4
31
32 #include "mutt.h"
33 #include "imap_private.h"
34 #include "auth.h"
35
36 /* imap_auth_gss: AUTH=GSSAPI support. */
37 imap_auth_res_t imap_auth_gss (IMAP_DATA * idata, const char *method __attribute__ ((unused)))
38 {
39   gss_buffer_desc request_buf, send_token;
40   gss_buffer_t sec_token;
41   gss_name_t target_name;
42   gss_ctx_id_t context;
43   gss_OID mech_name;
44   gss_qop_t quality;
45   int cflags;
46   OM_uint32 maj_stat, min_stat;
47   char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
48   unsigned long buf_size;
49   int rc;
50
51   if (!mutt_bit_isset (idata->capabilities, AGSSAPI))
52     return IMAP_AUTH_UNAVAIL;
53
54   if (mutt_account_getuser (&idata->conn->account))
55     return IMAP_AUTH_FAILURE;
56
57   /* get an IMAP service ticket for the server */
58   snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->account.host);
59   request_buf.value = buf1;
60   request_buf.length = m_strlen(buf1) + 1;
61   maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name,
62                               &target_name);
63   if (maj_stat != GSS_S_COMPLETE) {
64     return IMAP_AUTH_UNAVAIL;
65   }
66
67   /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */
68   sec_token = GSS_C_NO_BUFFER;
69   context = GSS_C_NO_CONTEXT;
70
71   /* build token */
72   maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
73                                    target_name, GSS_C_NO_OID,
74                                    GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, 0,
75                                    GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL,
76                                    &send_token, (unsigned int *) &cflags,
77                                    NULL);
78   if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
79     gss_release_name (&min_stat, &target_name);
80
81     return IMAP_AUTH_UNAVAIL;
82   }
83
84   /* now begin login */
85   mutt_message _("Authenticating (GSSAPI)...");
86
87   imap_cmd_start (idata, "AUTHENTICATE GSSAPI");
88
89   /* expect a null continuation response ("+") */
90   do
91     rc = imap_cmd_step (idata);
92   while (rc == IMAP_CMD_CONTINUE);
93
94   if (rc != IMAP_CMD_RESPOND) {
95     gss_release_name (&min_stat, &target_name);
96     goto bail;
97   }
98
99   /* now start the security context initialisation loop... */
100   mutt_to_base64 ((unsigned char *) buf1, send_token.value, send_token.length,
101                   sizeof (buf1) - 2);
102   gss_release_buffer (&min_stat, &send_token);
103   m_strcat(buf1, sizeof(buf1), "\r\n");
104   mutt_socket_write (idata->conn, buf1);
105
106   while (maj_stat == GSS_S_CONTINUE_NEEDED) {
107     /* Read server data */
108     do
109       rc = imap_cmd_step (idata);
110     while (rc == IMAP_CMD_CONTINUE);
111
112     if (rc != IMAP_CMD_RESPOND) {
113       gss_release_name (&min_stat, &target_name);
114       goto bail;
115     }
116
117     request_buf.length = mutt_from_base64 (buf2, idata->cmd.buf + 2);
118     request_buf.value = buf2;
119     sec_token = &request_buf;
120
121     /* Write client data */
122     maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
123                                      target_name, GSS_C_NO_OID,
124                                      GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
125                                      0, GSS_C_NO_CHANNEL_BINDINGS, sec_token,
126                                      NULL, &send_token,
127                                      (unsigned int *) &cflags, NULL);
128     if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
129       gss_release_name (&min_stat, &target_name);
130
131       goto err_abort_cmd;
132     }
133     mutt_to_base64 ((unsigned char *) buf1, send_token.value,
134                     send_token.length, sizeof (buf1) - 2);
135     gss_release_buffer (&min_stat, &send_token);
136     m_strcat(buf1, sizeof(buf1), "\r\n");
137     mutt_socket_write (idata->conn, buf1);
138   }
139
140   gss_release_name (&min_stat, &target_name);
141
142   /* get security flags and buffer size */
143   do
144     rc = imap_cmd_step (idata);
145   while (rc == IMAP_CMD_CONTINUE);
146
147   if (rc != IMAP_CMD_RESPOND) {
148     goto bail;
149   }
150   request_buf.length = mutt_from_base64 (buf2, idata->cmd.buf + 2);
151   request_buf.value = buf2;
152
153   maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token,
154                          &cflags, &quality);
155   if (maj_stat != GSS_S_COMPLETE) {
156     gss_release_buffer (&min_stat, &send_token);
157     goto err_abort_cmd;
158   }
159
160   /* first octet is security levels supported. We want NONE */
161   server_conf_flags = ((char *) send_token.value)[0];
162   if (!(((char *) send_token.value)[0] & GSS_AUTH_P_NONE)) {
163     gss_release_buffer (&min_stat, &send_token);
164     goto err_abort_cmd;
165   }
166
167   /* we don't care about buffer size if we don't wrap content. But here it is */
168   ((char *) send_token.value)[0] = 0;
169   buf_size = ntohl (*((long *) send_token.value));
170   gss_release_buffer (&min_stat, &send_token);
171
172   /* agree to terms (hack!) */
173   buf_size = htonl (buf_size);  /* not relevant without integrity/privacy */
174   memcpy (buf1, &buf_size, 4);
175   buf1[0] = GSS_AUTH_P_NONE;
176   /* server decides if principal can log in as user */
177   strncpy (buf1 + 4, idata->conn->account.user, sizeof (buf1) - 4);
178   request_buf.value = buf1;
179   request_buf.length = 4 + m_strlen(idata->conn->account.user) + 1;
180   maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
181                        &cflags, &send_token);
182   if (maj_stat != GSS_S_COMPLETE) {
183     goto err_abort_cmd;
184   }
185
186   mutt_to_base64 ((unsigned char *) buf1, send_token.value, send_token.length,
187                   sizeof (buf1) - 2);
188   m_strcat(buf1, sizeof(buf1), "\r\n");
189   mutt_socket_write (idata->conn, buf1);
190
191   /* Joy of victory or agony of defeat? */
192   do
193     rc = imap_cmd_step (idata);
194   while (rc == IMAP_CMD_CONTINUE);
195   if (rc == IMAP_CMD_RESPOND) {
196     goto err_abort_cmd;
197   }
198   if (imap_code (idata->cmd.buf)) {
199     /* flush the security context */
200     maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token);
201
202     /* send_token may contain a notification to the server to flush
203      * credentials. RFC 1731 doesn't specify what to do, and since this
204      * support is only for authentication, we'll assume the server knows
205      * enough to flush its own credentials */
206     gss_release_buffer (&min_stat, &send_token);
207
208     return IMAP_AUTH_SUCCESS;
209   }
210   else
211     goto bail;
212
213 err_abort_cmd:
214   mutt_socket_write (idata->conn, "*\r\n");
215   do
216     rc = imap_cmd_step (idata);
217   while (rc == IMAP_CMD_CONTINUE);
218
219 bail:
220   mutt_error _("GSSAPI authentication failed.");
221   mutt_sleep (2);
222   return IMAP_AUTH_FAILURE;
223 }
224
225 #endif /* USE_GSS */