1/*
2 * Copyright (c) 2004,2006,2008 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * krbtool.cpp - basic PKINIT tool
25 *
26 * Created 20 May 2004 by Doug Mitchell.
27 */
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <sys/types.h>
33#include <pwd.h>
34#include "identPicker.h"
35#include "asnUtils.h"
36#include <Kerberos/KerberosLogin.h>
37#include <Kerberos/pkinit_cert_store.h>
38
39static void usage(char **argv)
40{
41    printf("Usage: op [option..]\n");
42    printf("Ops:\n");
43    printf("   l   login\n");
44    printf("   s   select PKINIT client cert\n");
45    printf("   d   display PKINIT client cert\n");
46    printf("   D   delete PKINIT client cert setting\n");
47    printf("Options:\n");
48    printf("  -p principal      -- default is current user for login\n");
49    printf("  -l                -- loop (for malloc debug)\n");
50    printf("   TBD\n");
51    exit(1);
52}
53
54typedef enum {
55    KTO_Login = 0,
56    KTO_Select,
57    KTO_Display,
58    KTO_DeleteCert
59} KrbToolOp;
60
61typedef struct {
62    const char *principal;
63    /* I'm sure others */
64} KrbToolArgs;
65
66static int pkinitLogin(
67    const KrbToolArgs *args)
68{
69    /* Get a principal string one way or the other */
70    const char *principalStr = args->principal;
71    bool freeStr = false;
72    int ourRtn = 0;
73    KLLoginOptions klOpts = NULL;
74
75    if(principalStr == NULL) {
76        struct passwd *pw = getpwuid(getuid ());
77	if(pw == NULL) {
78	    printf("***Sorry, can't find current user info. Aborting.\n");
79	    return -1;
80	}
81	principalStr = strdup(pw->pw_name);
82	freeStr = true;
83    }
84
85    KLPrincipal principal = NULL;
86    KLStatus klrtn;
87
88    klrtn = KLCreatePrincipalFromString(principalStr, kerberosVersion_V5, &principal);
89    if(klrtn) {
90	printf("***KLCreatePrincipalFromString returned %d. Aborting.\n", (int)klrtn);
91	ourRtn = -1;
92	goto errOut;
93    }
94
95    /* Options, later maybe */
96    /* FIXME - don't know if the login options arg is optional */
97
98    /* By convention we use a non-NULL string as password; this may change later */
99    printf("...attempting TGT acquisition\n");
100    klrtn = KLAcquireNewInitialTicketsWithPassword(principal, klOpts, " ", NULL);
101    if(klrtn) {
102	printf("***KLAcquireInitialTicketsWithPassword returned %d\n", (int)klrtn);
103    }
104    else {
105	printf("...TGT acquisition successful.\n");
106    }
107errOut:
108    if(freeStr && (principalStr != NULL)) {
109	free((void *)principalStr);
110    }
111    if(klOpts != NULL) {
112	KLDisposeLoginOptions(klOpts);
113    }
114    if(principal != NULL) {
115	KLDisposePrincipal(principal);
116    }
117    return ourRtn;
118}
119
120static int pkinitSelect(
121    const KrbToolArgs *args)
122{
123    OSStatus ortn;
124    SecIdentityRef idRef = NULL;
125
126    if(args->principal == NULL) {
127	printf("***You must supply a principal name for this operation.\n");
128	return -1;
129    }
130    ortn = simpleIdentPicker(NULL, &idRef);
131    switch(ortn) {
132	case CSSMERR_CSSM_USER_CANCELED:
133	    printf("...operation terminated with no change to your settings.\n");
134	    return 0;
135	case noErr:
136	    break;
137	default:
138	    printf("***Operation aborted.\n");
139	    return -1;
140    }
141
142    krb5_error_code krtn = krb5_pkinit_set_client_cert(args->principal,
143	(krb5_pkinit_signing_cert_t)idRef);
144    if(krtn) {
145	cssmPerror("krb5_pkinit_set_client_cert", krtn);
146    }
147    else {
148	printf("...PKINIT client cert selection successful.\n\n");
149    }
150    if(idRef) {
151	CFRelease(idRef);
152    }
153    return 0;
154}
155
156static int pkinitDisplay(
157    const KrbToolArgs *args)
158{
159    krb5_pkinit_signing_cert_t idRef = NULL;
160    krb5_error_code krtn;
161
162    if(args->principal == NULL) {
163	printf("***You must supply a principal name for this operation.\n");
164	return -1;
165    }
166    krtn = krb5_pkinit_get_client_cert(args->principal, &idRef);
167    switch(krtn) {
168	case errSecItemNotFound:
169	    printf("...No PKINIT client cert configured for %s.\n",
170		args->principal ? args->principal : "Default");
171	    break;
172	case noErr:
173	{
174	    SecCertificateRef certRef = NULL;
175	    OSStatus ortn;
176	    CSSM_DATA cdata;
177
178	    ortn = SecIdentityCopyCertificate((SecIdentityRef)idRef, &certRef);
179	    if(ortn) {
180		cssmPerror("SecIdentityCopyCertificate", ortn);
181		break;
182	    }
183	    ortn = SecCertificateGetData(certRef, &cdata);
184	    if(ortn) {
185		cssmPerror("SecCertificateGetData", ortn);
186		break;
187	    }
188	    printf("--------- PKINIT Client Certificate ---------\n");
189	    printCertName(cdata.Data, cdata.Length, NameBoth);
190	    printf("---------------------------------------------\n\n");
191
192	    char *cert_hash = NULL;
193	    krb5_data kcert = {0, cdata.Length, (char *)cdata.Data};
194	    cert_hash = krb5_pkinit_cert_hash_str(&kcert);
195	    if(cert_hash == NULL) {
196		printf("***Error obtaining cert hash\n");
197	    }
198	    else {
199		printf("Cert hash string : %s\n\n", cert_hash);
200		free(cert_hash);
201	    }
202	    CFRelease(certRef);
203	    break;
204	}
205	default:
206	    cssmPerror("krb5_pkinit_get_client_cert", krtn);
207	    printf("***Error obtaining client cert\n");
208	    break;
209    }
210    if(idRef) {
211	krb5_pkinit_release_cert(idRef);
212    }
213
214    return 0;
215}
216
217static int pkinitDeleteCert(
218    const KrbToolArgs *args)
219{
220    krb5_error_code krtn;
221
222    krtn = krb5_pkinit_set_client_cert(args->principal, NULL);
223    if(krtn) {
224	cssmPerror("krb5_pkinit_set_client_cert(NULL)", krtn);
225	printf("***Error deleting client cert entry\n");
226    }
227    else {
228	printf("...client cert setting for %s deleted\n", args->principal ?
229	    args->principal : "Default principal");
230    }
231    return krtn;
232}
233
234int main(int argc, char **argv)
235{
236    if(argc < 2) {
237	usage(argv);
238    }
239
240    KrbToolOp op = KTO_Login;
241    switch(argv[1][0]) {
242	case 'l':
243	    op = KTO_Login;
244	    break;
245	case 's':
246	    op = KTO_Select;
247	    break;
248	case 'd':
249	    op = KTO_Display;
250	    break;
251	case 'D':
252	    op = KTO_DeleteCert;
253	    break;
254	default:
255	    usage(argv);
256    }
257
258    extern int optind;
259    extern char *optarg;
260    int arg;
261    int ourRtn = 0;
262    KrbToolArgs args;
263    bool doLoop = false;
264
265    memset(&args, 0, sizeof(args));
266    optind = 2;
267    while ((arg = getopt(argc, argv, "p:lh")) != -1) {
268	switch (arg) {
269	    case 'p':
270		args.principal = optarg;
271		break;
272	    case 'l':
273		doLoop = true;
274		break;
275	    case 'h':
276	    default:
277		usage(argv);
278	}
279    }
280
281    do {
282	switch(op) {
283	    case KTO_Login:
284		ourRtn = pkinitLogin(&args);
285		break;
286	    case KTO_Select:
287		ourRtn = pkinitSelect(&args);
288		break;
289	    case KTO_Display:
290		ourRtn = pkinitDisplay(&args);
291		break;
292	    case KTO_DeleteCert:
293		ourRtn = pkinitDeleteCert(&args);
294		break;
295	    default:
296		printf("***BRRZAP! Internal error.\n");
297		exit(1);
298	}
299	if(doLoop) {
300	    char resp;
301
302	    fpurge(stdin);
303	    printf("q to quit, anything else to loop: ");
304	    resp = getchar();
305	    if(resp == 'q') {
306		break;
307	    }
308	}
309    } while(doLoop);
310    /* cleanup */
311    return ourRtn;
312
313}
314