1/* 2 * Copyright (c) 2004 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * 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 the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include <popper.h> 35#include <base64.h> 36#include <pop_auth.h> 37RCSID("$Id$"); 38 39 40#if defined(SASL) && defined(KRB5) 41#include <gssapi.h> 42 43extern krb5_context gssapi_krb5_context; 44 45struct gss_state { 46 gss_ctx_id_t context_hdl; 47 gss_OID mech_oid; 48 gss_name_t client_name; 49 int stage; 50}; 51 52static void 53gss_set_error (struct gss_state *gs, int min_stat) 54{ 55 OM_uint32 new_stat; 56 OM_uint32 msg_ctx = 0; 57 gss_buffer_desc status_string; 58 OM_uint32 ret; 59 60 do { 61 char * cstr; 62 63 ret = gss_display_status (&new_stat, 64 min_stat, 65 GSS_C_MECH_CODE, 66 gs->mech_oid, 67 &msg_ctx, 68 &status_string); 69 if (asprintf(&cstr, "%.*s", (int)status_string.length, 70 (const char *)status_string.value) >= 0) { 71 pop_auth_set_error(cstr); 72 free(cstr); 73 } else { 74 pop_auth_set_error("unknown error"); 75 } 76 gss_release_buffer (&new_stat, &status_string); 77 } while (!GSS_ERROR(ret) && msg_ctx != 0); 78} 79 80static int 81gss_loop(POP *p, void *state, 82 /* const */ void *input, size_t input_length, 83 void **output, size_t *output_length) 84{ 85 struct gss_state *gs = state; 86 gss_buffer_desc real_input_token, real_output_token; 87 gss_buffer_t input_token = &real_input_token, 88 output_token = &real_output_token; 89 OM_uint32 maj_stat, min_stat; 90 gss_channel_bindings_t bindings = GSS_C_NO_CHANNEL_BINDINGS; 91 92 if(gs->stage == 0) { 93 /* we require an initial response, so ask for one if not 94 present */ 95 gs->stage++; 96 if(input == NULL && input_length == 0) { 97 /* XXX this could be done better */ 98 fputs("+ \r\n", p->output); 99 fflush(p->output); 100 return POP_AUTH_CONTINUE; 101 } 102 } 103 if(gs->stage == 1) { 104 input_token->value = input; 105 input_token->length = input_length; 106 maj_stat = 107 gss_accept_sec_context (&min_stat, 108 &gs->context_hdl, 109 GSS_C_NO_CREDENTIAL, 110 input_token, 111 bindings, 112 &gs->client_name, 113 &gs->mech_oid, 114 output_token, 115 NULL, 116 NULL, 117 NULL); 118 if (GSS_ERROR(maj_stat)) { 119 gss_set_error(gs, min_stat); 120 return POP_AUTH_FAILURE; 121 } 122 if (output_token->length != 0) { 123 *output = output_token->value; 124 *output_length = output_token->length; 125 } 126 if(maj_stat == GSS_S_COMPLETE) 127 gs->stage++; 128 129 return POP_AUTH_CONTINUE; 130 } 131 132 if(gs->stage == 2) { 133 /* send wanted protection levels */ 134 unsigned char x[4] = { 1, 0, 0, 0 }; 135 136 input_token->value = x; 137 input_token->length = 4; 138 139 maj_stat = gss_wrap(&min_stat, 140 gs->context_hdl, 141 FALSE, 142 GSS_C_QOP_DEFAULT, 143 input_token, 144 NULL, 145 output_token); 146 if (GSS_ERROR(maj_stat)) { 147 gss_set_error(gs, min_stat); 148 return POP_AUTH_FAILURE; 149 } 150 *output = output_token->value; 151 *output_length = output_token->length; 152 gs->stage++; 153 return POP_AUTH_CONTINUE; 154 } 155 if(gs->stage == 3) { 156 /* receive protection levels and username */ 157 char *name; 158 krb5_principal principal; 159 gss_buffer_desc export_name; 160 gss_OID oid; 161 unsigned char *ptr; 162 163 input_token->value = input; 164 input_token->length = input_length; 165 166 maj_stat = gss_unwrap (&min_stat, 167 gs->context_hdl, 168 input_token, 169 output_token, 170 NULL, 171 NULL); 172 if (GSS_ERROR(maj_stat)) { 173 gss_set_error(gs, min_stat); 174 return POP_AUTH_FAILURE; 175 } 176 if(output_token->length < 5) { 177 pop_auth_set_error("response too short"); 178 return POP_AUTH_FAILURE; 179 } 180 ptr = output_token->value; 181 if(ptr[0] != 1) { 182 pop_auth_set_error("must use clear text"); 183 return POP_AUTH_FAILURE; 184 } 185 memmove(output_token->value, ptr + 4, output_token->length - 4); 186 ptr[output_token->length - 4] = '\0'; 187 188 maj_stat = gss_display_name(&min_stat, gs->client_name, 189 &export_name, &oid); 190 if(maj_stat != GSS_S_COMPLETE) { 191 gss_set_error(gs, min_stat); 192 return POP_AUTH_FAILURE; 193 } 194 /* XXX kerberos */ 195 if(oid != GSS_KRB5_NT_PRINCIPAL_NAME) { 196 pop_auth_set_error("unexpected gss name type"); 197 gss_release_buffer(&min_stat, &export_name); 198 return POP_AUTH_FAILURE; 199 } 200 name = malloc(export_name.length + 1); 201 if(name == NULL) { 202 pop_auth_set_error("out of memory"); 203 gss_release_buffer(&min_stat, &export_name); 204 return POP_AUTH_FAILURE; 205 } 206 memcpy(name, export_name.value, export_name.length); 207 name[export_name.length] = '\0'; 208 gss_release_buffer(&min_stat, &export_name); 209 krb5_parse_name(gssapi_krb5_context, name, &principal); 210 211 if(!krb5_kuserok(gssapi_krb5_context, principal, ptr)) { 212 pop_auth_set_error("Permission denied"); 213 return POP_AUTH_FAILURE; 214 } 215 216 217 strlcpy(p->user, ptr, sizeof(p->user)); 218 return POP_AUTH_COMPLETE; 219 } 220 return POP_AUTH_FAILURE; 221} 222 223 224static int 225gss_init(POP *p, void **state) 226{ 227 struct gss_state *gs = malloc(sizeof(*gs)); 228 if(gs == NULL) { 229 pop_auth_set_error("out of memory"); 230 return POP_AUTH_FAILURE; 231 } 232 gs->context_hdl = GSS_C_NO_CONTEXT; 233 gs->stage = 0; 234 *state = gs; 235 return POP_AUTH_CONTINUE; 236} 237 238static int 239gss_cleanup(POP *p, void *state) 240{ 241 OM_uint32 min_stat; 242 struct gss_state *gs = state; 243 if(gs->context_hdl != GSS_C_NO_CONTEXT) 244 gss_delete_sec_context(&min_stat, &gs->context_hdl, GSS_C_NO_BUFFER); 245 free(state); 246 return POP_AUTH_CONTINUE; 247} 248 249struct auth_mech gssapi_mech = { 250 "GSSAPI", gss_init, gss_loop, gss_cleanup 251}; 252 253#endif /* KRB5 */ 254