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