1 /**
2 * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 **/
16
17 #include <Python.h>
18 #include "kerberosbasic.h"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #undef PRINTFS
25
26 extern PyObject *BasicAuthException_class;
27 static void set_basicauth_error(krb5_context context, krb5_error_code code);
28
29 static krb5_error_code verify_krb5_user(
30 krb5_context context, krb5_principal principal, const char *password,
31 krb5_principal server
32 );
33
authenticate_user_krb5pwd(const char * user,const char * pswd,const char * service,const char * default_realm)34 int authenticate_user_krb5pwd(
35 const char *user, const char *pswd, const char *service,
36 const char *default_realm
37 ) {
38 krb5_context kcontext = NULL;
39 krb5_error_code code;
40 krb5_principal client = NULL;
41 krb5_principal server = NULL;
42 int ret = 0;
43 char *name = NULL;
44 char *p = NULL;
45
46 code = krb5_init_context(&kcontext);
47 if (code)
48 {
49 PyErr_SetObject(
50 BasicAuthException_class,
51 Py_BuildValue(
52 "((s:i))", "Cannot initialize Kerberos5 context", code
53 )
54 );
55 return 0;
56 }
57
58 ret = krb5_parse_name (kcontext, service, &server);
59
60 if (ret) {
61 set_basicauth_error(kcontext, ret);
62 ret = 0;
63 goto end;
64 }
65
66 code = krb5_unparse_name(kcontext, server, &name);
67 if (code) {
68 set_basicauth_error(kcontext, code);
69 ret = 0;
70 goto end;
71 }
72 #ifdef PRINTFS
73 printf("Using %s as server principal for password verification\n", name);
74 #endif
75 free(name);
76 name = NULL;
77
78 name = (char *)malloc(256);
79 if (name == NULL)
80 {
81 PyErr_NoMemory();
82 ret = 0;
83 goto end;
84 }
85 p = strchr(user, '@');
86 if (p == NULL) {
87 snprintf(name, 256, "%s@%s", user, default_realm);
88 } else {
89 snprintf(name, 256, "%s", user);
90 }
91
92 code = krb5_parse_name(kcontext, name, &client);
93 if (code) {
94 set_basicauth_error(kcontext, code);
95 ret = 0;
96 goto end;
97 }
98
99 code = verify_krb5_user(kcontext, client, pswd, server);
100
101 if (code) {
102 ret = 0;
103 goto end;
104 }
105
106 ret = 1;
107
108 end:
109 #ifdef PRINTFS
110 printf(
111 "kerb_authenticate_user_krb5pwd ret=%d user=%s authtype=%s\n",
112 ret, user, "Basic"
113 );
114 #endif
115 if (name) {
116 free(name);
117 }
118 if (client) {
119 krb5_free_principal(kcontext, client);
120 }
121 if (server) {
122 krb5_free_principal(kcontext, server);
123 }
124 krb5_free_context(kcontext);
125
126 return ret;
127 }
128
129 /* Inspired by krb5_verify_user from Heimdal */
verify_krb5_user(krb5_context context,krb5_principal principal,const char * password,krb5_principal server)130 static krb5_error_code verify_krb5_user(
131 krb5_context context, krb5_principal principal, const char *password,
132 krb5_principal server
133 ) {
134 krb5_creds creds;
135 krb5_get_init_creds_opt gic_options;
136 krb5_error_code ret;
137 char *name = NULL;
138
139 memset(&creds, 0, sizeof(creds));
140
141 ret = krb5_unparse_name(context, principal, &name);
142 if (ret == 0) {
143 #ifdef PRINTFS
144 printf("Trying to get TGT for user %s\n", name);
145 #endif
146 free(name);
147 }
148
149 krb5_get_init_creds_opt_init(&gic_options);
150 ret = krb5_get_init_creds_password(
151 context, &creds, principal, (char *)password,
152 NULL, NULL, 0, NULL, &gic_options
153 );
154 if (ret) {
155 set_basicauth_error(context, ret);
156 goto end;
157 }
158
159 end:
160 krb5_free_cred_contents(context, &creds);
161
162 return ret;
163 }
164
set_basicauth_error(krb5_context context,krb5_error_code code)165 static void set_basicauth_error(krb5_context context, krb5_error_code code)
166 {
167 PyErr_SetObject(
168 BasicAuthException_class,
169 Py_BuildValue("(s:i)", krb5_get_err_text(context, code), code)
170 );
171 }
172