diff options
Diffstat (limited to 'src/kadmin/client/kadmin.c')
-rw-r--r-- | src/kadmin/client/kadmin.c | 773 |
1 files changed, 773 insertions, 0 deletions
diff --git a/src/kadmin/client/kadmin.c b/src/kadmin/client/kadmin.c new file mode 100644 index 0000000000..91887d8a8c --- /dev/null +++ b/src/kadmin/client/kadmin.c @@ -0,0 +1,773 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + */ + +/* + * Sandia National Laboratories also makes no representations about the + * suitability of the modifications, or additions to this software for + * any purpose. It is provided "as is" without express or implied warranty. + */ + +#if !defined(lint) && !defined(SABER) +static char rcsid_kadmin[] = + "$Header$"; +#endif /* lint */ + +/* + * kadmin + * Perform Remote Kerberos Administrative Functions + */ + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <signal.h> +#ifndef __convex__ +#include <strings.h> +#endif +#include <pwd.h> +#include <com_err.h> + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#include <krb5/adm_defs.h> + +#include <krb5/krb5.h> +#include <krb5/ext-proto.h> +#include <krb5/los-proto.h> +#include <krb5/kdb.h> +#include <krb5/kdb_dbm.h> + +krb5_error_code get_first_ticket + PROTOTYPE((krb5_ccache, + krb5_principal)); + +struct sockaddr_in local_sin, remote_sin; + +krb5_creds my_creds; + +void get_def_princ(); + +main(argc,argv) + int argc; + char *argv[]; +{ + extern char *optarg; + + krb5_ccache cache = NULL; + char cache_name[255]; + + krb5_address local_addr, foreign_addr; + + krb5_principal client; + + char *client_name; /* Single string representation of client id */ + + krb5_data *requested_realm; + + krb5_error_code retval; /* return code */ + + int local_socket; + + krb5_error *err_ret; + krb5_ap_rep_enc_part *rep_ret; + + kadmin_requests rd_priv_resp; + + krb5_checksum send_cksum; + krb5_data msg_data, inbuf; + krb5_int32 seqno; + char buffer[255]; + char command_type[120]; + char princ_name[120]; + int i, valid; + int option; + int oper_type; + + krb5_init_ets(); + client_name = (char *) malloc(755); + memset((char *) client_name, 0, sizeof(client_name)); + + if (argc > 3) + usage(); + + if (argc == 1) { /* No User Specified */ + get_def_princ(&client); + strcpy(client_name, client->data[0].data); + strncat(client_name, "/admin@", 7); + strncat(client_name, client->realm.data, client->realm.length); + if (retval = krb5_parse_name(client_name, &client)) { + fprintf(stderr, "Unable to Parse Client Name!\n"); + usage(); + } + } + else { + while ((option = getopt(argc, argv, "n")) != EOF) { + switch (option) { + case 'n': + if (argc == 3) { + strcpy(client_name, argv[2]); + if (retval = krb5_parse_name(client_name, &client)) { + fprintf(stderr, "Unable to Parse Client Name!\n"); + usage(); + } + } + else { + get_def_princ(&client); + if (retval = krb5_unparse_name(client, &client_name)) { + fprintf(stderr, "Unable to unparse Client Name!\n"); + usage(); + } + } + break; + case '?': + default: + usage(); + break; + } + } + + if (client_name[0] == '\0') { /* No -n option specified */ + if (argc > 2) + usage(); + strcpy(client_name, argv[1]); + if (!strncmp("help", client_name, strlen(client_name))) + usage(); + if (!strncmp("root", client_name, strlen(client_name))) { + fprintf(stderr, "root is not a valid Administrator!\n\n"); + usage(); + } + if (retval = krb5_parse_name(client_name, &client)) { + fprintf(stderr, "Error Parsing User Specified Name Option!\n"); + exit(1); + } + } + } /* switch */ + + /* Create credential cache for kadmin */ + (void) sprintf(cache_name, "FILE:/tmp/tkt_adm_%d", getpid()); + + if ((retval = krb5_cc_resolve(cache_name, &cache))) { + fprintf(stderr, "Unable to Resolve Cache: !\n", cache_name); + } + + if ((retval = krb5_cc_initialize(cache, client))) { + fprintf(stderr, "Error initializing cache: %s!\n", cache_name); + exit(1); + } + +/* + * Verify User by Obtaining Initial Credentials prior to Initial Link + */ + + if ((retval = get_first_ticket(cache, client))) { + (void) krb5_cc_destroy(cache); + exit(1); + } + /* my_creds has the necessary credentials for further processing: + Destroy credential cache for security reasons */ + (void) krb5_cc_destroy(cache); + + requested_realm = (krb5_data *) &client->realm; + + + /* Initiate Link to Server */ + if ((retval = adm5_init_link(requested_realm, &local_socket))) { + (void) krb5_cc_destroy(cache); + exit(1); + } + +#ifdef unicos61 +#define SIZEOF_INADDR SIZEOF_in_addr +#else +#define SIZEOF_INADDR sizeof(struct in_addr) +#endif + +/* V4 kpasswd Protocol Hack + * Necessary for ALL kadmind clients + */ + { + int msg_length = 0; + + retval = krb5_net_write(local_socket, (char *) &msg_length + 2, 2); + if (retval < 0) { + fprintf(stderr, "krb5_net_write failure!\n"); + (void) krb5_cc_destroy(cache); + exit(1); + } + } + + local_addr.addrtype = ADDRTYPE_INET; + local_addr.length = SIZEOF_INADDR ; + local_addr.contents = (krb5_octet *) &local_sin.sin_addr; + + foreign_addr.addrtype = ADDRTYPE_INET; + foreign_addr.length = SIZEOF_INADDR ; + foreign_addr.contents = (krb5_octet *) &remote_sin.sin_addr; + + /* compute checksum, using CRC-32 */ + if (!(send_cksum.contents = (krb5_octet *) + malloc(krb5_checksum_size(CKSUMTYPE_CRC32)))) { + fprintf(stderr, "Insufficient Memory while Allocating Checksum!\n"); + (void) krb5_cc_destroy(cache); + exit(1); + } + + /* choose some random stuff to compute checksum from */ + if (retval = krb5_calculate_checksum(CKSUMTYPE_CRC32, + ADM5_ADM_VERSION, + strlen(ADM5_ADM_VERSION), + 0, + 0, /* if length is 0, crc-32 doesn't + use the seed */ + &send_cksum)) { + fprintf(stderr, "Error while Computing Checksum: %s!\n", + error_message(retval)); + free(send_cksum.contents); + (void) krb5_cc_destroy(cache); + exit(1); + } + + /* call Kerberos library routine to obtain an authenticator, + pass it over the socket to the server, and obtain mutual + authentication. */ + + if ((retval = krb5_sendauth((krb5_pointer) &local_socket, + ADM_CPW_VERSION, + my_creds.client, + my_creds.server, + AP_OPTS_MUTUAL_REQUIRED, + &send_cksum, + &my_creds, + 0, + &seqno, + 0, /* don't need a subsession key */ + &err_ret, + &rep_ret))) { + fprintf(stderr, "Error while performing sendauth: %s!\n", + error_message(retval)); + free(send_cksum.contents); + exit(1); + } + + /* Read back what the server has to say ... */ + if (retval = krb5_read_message(&local_socket, &inbuf)){ + fprintf(stderr, " Read Message Error: %s!\n", + error_message(retval)); + free(send_cksum.contents); + exit(1); + } + + if ((inbuf.length != 2) || (inbuf.data[0] != KADMIND) || + (inbuf.data[1] != KADMSAG)){ + fprintf(stderr, " Invalid ack from admin server.!\n"); + free(send_cksum.contents); + exit(1); + } + free(inbuf.data); + + if ((inbuf.data = (char *) calloc(1, 2)) == (char *) 0) { + fprintf(stderr, "No memory for command!\n"); + free(send_cksum.contents); + exit(1); + } + + inbuf.data[0] = KADMIN; + inbuf.data[1] = 0xff; + inbuf.length = 2; + + if ((retval = krb5_mk_priv(&inbuf, + ETYPE_DES_CBC_CRC, + &my_creds.keyblock, + &local_addr, + &foreign_addr, + seqno, + KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME, + 0, + 0, + &msg_data))) { + fprintf(stderr, "Error during First Message Encoding: %s!\n", + error_message(retval)); + free(inbuf.data); + free(send_cksum.contents); + exit(1); + } + free(inbuf.data); + + /* write private message to server */ + if (krb5_write_message(&local_socket, &msg_data)){ + fprintf(stderr, "Write Error During First Message Transmission!\n"); + free(send_cksum.contents); + exit(1); + } + free(msg_data.data); + + for ( ; ; ) { + /* Ok Now let's get the private message */ + if (retval = krb5_read_message(&local_socket, &inbuf)){ + fprintf(stderr, "Read Error During First Reply: %s!\n", + error_message(retval)); + free(send_cksum.contents); + exit(1); + } + + if ((retval = krb5_rd_priv(&inbuf, + &my_creds.keyblock, + &foreign_addr, + &local_addr, + rep_ret->seq_number, + KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME, + 0, + 0, + &msg_data))) { + fprintf(stderr, "Error during First Read Decoding: %s!\n", + error_message(retval)); + free(send_cksum.contents); + exit(1); + } + free(inbuf.data); + + valid = 0; + princ_name[0] = '\0'; +repeat: + printf("\n\nCommand (add, cpw, del, inq, mod, addrnd, cpwrnd, addv4, cpwv4, q): "); + fgets(buffer, sizeof(buffer), stdin); + buffer[strlen(buffer) -1] = '\0'; + sscanf(buffer,"%s %s", command_type, princ_name); + for (i = 0; command_type[i] != '\0'; i++) + if (isupper(command_type[i])) + command_type[i] = tolower(command_type[i]); + + if (!strcmp(command_type, "add")) { + valid++; + oper_type = ADDOPER; + if (retval = kadm_add_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + oper_type, + princ_name)) break; + } + if (!strcmp(command_type, "cpw")) { + valid++; + oper_type = CHGOPER; + if (retval = kadm_cpw_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + oper_type, + princ_name)) break; + } + if (!strcmp(command_type, "addrnd")) { + valid++; + if (retval = kadm_add_user_rnd(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + princ_name)) break; + } + if (!strcmp(command_type, "cpwrnd")) { + valid++; + if (retval = kadm_cpw_user_rnd(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + princ_name)) break; + } + if (!strcmp(command_type, "del")) { + valid++; + if (retval = kadm_del_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + princ_name)) break; + } + if (!strcmp(command_type, "inq")) { + valid++; + if (retval = kadm_inq_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + princ_name)) break; + } + if (!strcmp(command_type, "mod")) { + valid++; + if (retval = kadm_mod_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + princ_name)) break; + } + if (!strcmp(command_type, "addv4")) { + valid++; + oper_type = AD4OPER; + if (retval = kadm_add_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + oper_type, + princ_name)) break; + } + if (!strcmp(command_type, "cpwv4")) { + valid++; + oper_type = CH4OPER; + if (retval = kadm_cpw_user(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno, + oper_type, + princ_name)) break; + } + if (!strcmp(command_type, "q")) { + valid++; + retval = kadm_done(&my_creds, + rep_ret, + &local_addr, + &foreign_addr, + &local_socket, + &seqno); + break; + } + + if (!valid) { + fprintf(stderr, "Invalid Input - Retry\n"); + goto repeat; + } + } + + if (retval) { + free(send_cksum.contents); + exit(1); + } + + /* Ok Now let's get the final private message */ + if (retval = krb5_read_message(&local_socket, &inbuf)){ + fprintf(stderr, "Read Error During Final Reply: %s!\n", + error_message(retval)); + free(send_cksum.contents); + exit(1); + } + + if ((retval = krb5_rd_priv(&inbuf, + &my_creds.keyblock, + &foreign_addr, + &local_addr, + rep_ret->seq_number, + KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME, + 0, + 0, + &msg_data))) { + fprintf(stderr, "Error during Final Read Decoding :%s!\n", + error_message(retval)); + free(send_cksum.contents); + free(inbuf.data); + exit(1); + } + free(inbuf.data); + + memcpy(&rd_priv_resp.appl_code, msg_data.data, 1); + memcpy(&rd_priv_resp.oper_code, msg_data.data + 1, 1); + memcpy(&rd_priv_resp.retn_code, msg_data.data + 2, 1); + + free(msg_data.data); + if (!((rd_priv_resp.appl_code == KADMIN) && + (rd_priv_resp.retn_code == KADMGOOD))) { + fprintf(stderr, "Generic Error During kadmin Termination!\n"); + retval = 1; + } else { + fprintf(stderr, "\nHave a Good Day.\n\n"); + } + + free(send_cksum.contents); + + + if (retval) { + fprintf(stderr, "\n\nkadmin terminating - %s.\n\n", + kadmind_kadmin_response[rd_priv_resp.retn_code]); + exit(1); + } + exit(0); +} + +krb5_error_code +get_first_ticket(DECLARG(krb5_ccache, cache), + DECLARG(krb5_principal, client)) +OLDDECLARG(krb5_ccache, cache) +OLDDECLARG(krb5_principal, client) +{ + char prompt[255]; /* for the password prompt */ + + krb5_address **my_addresses; + + char *client_name; + krb5_error_code retval; + char *password; + int pwsize; + + if ((retval = krb5_unparse_name(client, &client_name))) { + fprintf(stderr, "Unable to Unparse Client Name!\n"); + return(1); + } + + if ((retval = krb5_os_localaddr(&my_addresses))) { + fprintf(stderr, "Unable to Get Principals Address!\n"); + return(1); + } + + memset((char *) &my_creds, 0, sizeof(my_creds)); + + my_creds.client = client; + + if ((retval = krb5_build_principal_ext(&my_creds.server, + client->realm.length, + client->realm.data, + strlen(CPWNAME), + CPWNAME, /* kadmin */ + client->realm.length, + client->realm.data, + /* instance is <realm> */ + 0))) { + fprintf(stderr, "Error %s while building client name!\n"); + krb5_free_addresses(my_addresses); + return(1); + } + + (void) sprintf(prompt,"Password for %s: ", (char *) client_name); + + if ((password = (char *) calloc (1, 255)) == NULL) { + fprintf(stderr, "No Memory for Retrieving Admin Password!\n"); + return(1); + } + + pwsize = 255; + if ((retval = krb5_read_password( + prompt, + 0, + password, + &pwsize) || pwsize == 0)) { + fprintf(stderr, "Error while reading password for '%s'!\n", + client_name); + free(password); + krb5_free_addresses(my_addresses); + return(1); + } + + /* Build Request for Initial Credentials */ + if ((retval = krb5_get_in_tkt_with_password( + 0, /* options */ + my_addresses, + KRB5_PADATA_ENC_RANDOM, /* do random preauth */ + ETYPE_DES_CBC_CRC, /* etype */ + KEYTYPE_DES, + password, + cache, + &my_creds, + 0 ))) { + fprintf(stderr, "\nUnable to Get Initial Credentials : %s!\n", + error_message(retval)); + (void) memset(password, 0, pwsize); + free(password); + krb5_free_addresses(my_addresses); + return(1); + } + + /* Do NOT Forget to zap password */ + memset((char *) password, 0, pwsize); + free(password); + krb5_free_addresses(my_addresses); + return(0); +} + +krb5_error_code +adm5_init_link( realm_of_server, local_socket) +krb5_data *realm_of_server; +int * local_socket; + +{ + struct servent *service_process; /* service we will talk to */ + struct hostent *remote_host; /* host we will talk to */ + char **hostlist; + int namelen; + int i; + + krb5_error_code retval; + + /* clear out the structure first */ + (void) memset((char *)&remote_sin, 0, sizeof(remote_sin)); + + if ((service_process = getservbyname(CPW_SNAME, "tcp")) == NULL) { + fprintf(stderr, "Unable to find Service (%s) Check services file!\n", + CPW_SNAME); + return(1); + } + + /* Copy the Port Number */ + remote_sin.sin_port = service_process->s_port; + + hostlist = 0; + + /* Identify all Hosts Associated with this Realm */ + if ((retval = krb5_get_krbhst (realm_of_server, &hostlist))) { + fprintf(stderr, "krb5_get_krbhst: Unable to Determine Server Name!\n"); + return(retval); + } + + if (hostlist[0] == 0) { + fprintf(stderr, "No hosts found!\n"); + return KRB5_REALM_UNKNOWN; + } + + for (i=0; hostlist[i]; i++) { + remote_host = gethostbyname(hostlist[i]); + if (remote_host != 0) { + + /* set up the address of the foreign socket for connect() */ + remote_sin.sin_family = remote_host->h_addrtype; + (void) memcpy((char *) &remote_sin.sin_addr, + (char *) remote_host->h_addr, + sizeof(remote_host->h_addr)); + break; /* Only Need one */ + } + } + + krb5_free_krbhst(hostlist); + + /* open a TCP socket */ + *local_socket = socket(PF_INET, SOCK_STREAM, 0); + if (*local_socket < 0) { + retval = errno; + fprintf(stderr, "Cannot Open Socket!\n"); + return retval; + } + /* connect to the server */ + if (connect(*local_socket, &remote_sin, sizeof(remote_sin)) < 0) { + retval = errno; + fprintf(stderr, "Cannot Connect to Socket!\n"); + close(*local_socket); + return retval; + } + + /* find out who I am, now that we are connected and therefore bound */ + namelen = sizeof(local_sin); + if (getsockname(*local_socket, + (struct sockaddr *) &local_sin, &namelen) < 0) { + retval = errno; + fprintf(stderr, "Cannot Perform getsockname!\n"); + close(*local_socket); + return retval; + } + return 0; +} + +void +get_def_princ(client) + krb5_principal *client; +{ + krb5_ccache cache = NULL; + struct passwd *pw; + int retval; + char client_name[755]; + krb5_flags cc_flags; + + /* Identify Default Credentials Cache */ + if (retval = krb5_cc_default(&cache)) { + fprintf(stderr, "Error while getting default ccache!\n"); + exit(1); + } + + /* + * Attempt to Modify Credentials Cache + * retval == 0 ==> ccache Exists - Use It + * retval == ENOENT ==> No Entries, but ccache Exists + * retval != 0 ==> Assume ccache does NOT Exist + */ + cc_flags = 0; + if (retval = krb5_cc_set_flags(cache, cc_flags)) { + /* Search passwd file for client */ + pw = getpwuid((int) getuid()); + if (pw) { + (void) strcpy(client_name, pw->pw_name); + if (!strncmp("root", client_name, strlen(client_name))) { + fprintf(stderr, + "root is not a valid Adimnistrator\n!\n"); + usage(); + } + } else { + fprintf(stderr, + "Unable to Identify Principal from Password File!\n"); + retval = 1; + usage(); + } + + /* Use this to get default_realm and format client_name */ + if ((retval = krb5_parse_name(client_name, client))) { + fprintf(stderr, "Unable to Parse Client Name!\n"); + usage(); + } + } else { + /* Read Client from Cache */ + if (retval = krb5_cc_get_principal(cache, client)) { + fprintf(stderr, + "Unable to Read Principal Credentials File!\n"); + exit(1); + } + + if (!strncmp("root", (*client)->data[0].data, + (*client)->data[0].length)) { + fprintf(stderr, "root is not a valid Administrator\n!\n"); + usage(); + } + + (void) krb5_cc_close(cache); + } +} + +usage() +{ + fprintf(stderr, "Usage: "); + fprintf(stderr, "kadmin [-n] [Administrator name]\n\n"); + fprintf(stderr, " If an Administrator name is not supplied, kadmin "); + fprintf(stderr, "will first\n attempt to locate the name from "); + fprintf(stderr, "the default ticket file, then\n by using the "); + fprintf(stderr, "username from the 'passwd' file.\n\n"); + fprintf(stderr, " For Cross Realm Obtain a ticket for 'Administrator "); + fprintf(stderr, "name' in the\n Destination realm or "); + fprintf(stderr, "specify the Destination Realm\n as part of the "); + fprintf(stderr, "Administrator name option.\n\n"); + fprintf(stderr, " Note: If the Administrator Name is not "); + fprintf(stderr, "supplied, then the \n"); + fprintf(stderr, " '/admin' instance will be appended to the "); + fprintf(stderr, "default name unless\n"); + fprintf(stderr, " the -n option is used.\n\n"); + exit(0); +} |