X-Git-Url: http://andersk.mit.edu/gitweb/moira.git/blobdiff_plain/f46fccfa8499d4cbdb9a25c9e56d87eadcde7062..1beb5e231e63b447477fd33ae065d464d545545d:/reg_svr/reg_svr.c diff --git a/reg_svr/reg_svr.c b/reg_svr/reg_svr.c index d1a2871d..60dd1006 100644 --- a/reg_svr/reg_svr.c +++ b/reg_svr/reg_svr.c @@ -1,727 +1,1067 @@ /* - * $Source$ - * $Author$ - * $Header$ + * $Source$ + * $Author$ + * $Header$ * - * Copyright (C) 1987 by the Massachusetts Institute of Technology + * Copyright (C) 1987, 1988 by the Massachusetts Institute of Technology + * For copying and distribution information, please see the file + * . * - * Server for user registration with SMS and Kerberos. + * Server for user registration with Moira and Kerberos. * - * This program is a client of the SMS server and the Kerberos - * admin_server, and is a server for the userreg program. - * - * $Log$ - * Revision 1.2 1987-08-22 18:39:45 wesommer - * User registration server. - * - * Revision 1.1 87/07/31 15:48:13 wesommer - * Initial revision - * + * This program is a client of the Kerberos admin_server and a + * server for the userreg program. It is not a client of the + * Moira server as it is linked with libmoiraglue which bypasses + * the network protocol. */ #ifndef lint static char *rcsid_reg_svr_c = "$Header$"; #endif lint +#include #include +#include +#include +#include #include #include -#include -#include -#include +#include #include +#include +#include +#include #include -#include -#include "ureg_err.h" -#include "ureg_proto.h" -#include "../../include/sms.h" -#include "admin_server.h" -#include "admin_err.h" -#include +#include "moira.h" +#include "moira_site.h" +#include "reg_svr.h" -extern void abort(); -extern char *strdup(); -extern char *malloc(); -extern int krb_err_base; extern char admin_errmsg[]; -long now; -#define STAMP { time (&now); printf(ctime(&now)); } - -struct msg { - u_long version; - u_long request; - char *first; - char *last; - char *sealed; - int sealed_len; -}; - -static char retval[BUFSIZ]; - -main() +void reg_com_err_hook(); + +main(argc,argv) + int argc; + char *argv[]; { - struct sockaddr_in sin; - struct servent *sp; - int s; - int status; - int addrlen, len; - char buf[BUFSIZ]; - extern int errno; - u_long seqno; - struct msg message; - extern char *whoami; + struct msg message; /* Storage for parsed packet */ + int status = SUCCESS; /* Error status */ + char retval[BUFSIZ]; /* Buffer to hold return message for client */ + + void req_initialize(); /* Initialize request layer */ + void get_request(); /* Get a request */ + void report(); /* Respond to a request */ + /* Initialize */ + whoami = argv[0]; + + /* Error messages sent one line at a time */ setlinebuf(stderr); - whoami = "reg_svr"; + setlinebuf(stdout); + set_com_err_hook(reg_com_err_hook); + /* Initialize com_err error tables */ init_ureg_err_tbl(); + init_krb_err_tbl(); + init_kadm_err_tbl(); + initialize_gdss_error_table(); - status = sms_connect(); - if (status != 0) { - com_err("reg_svr", status, " on connect"); - exit(1); - } - status = sms_auth(); - if (status != 0) { - com_err("reg_svr", status, " on auth"); - exit(1); - } + /* Use com_err or output to stderr for all log messages. */ +#ifdef DEBUG + com_err(whoami, 0, "*** Debugging messages enabled. ***"); +#endif DEBUG - sp = getservbyname("sms_ureg", "udp"); - if (sp == NULL) { - fprintf(stderr, "Unknown service sms_ureg/udp\n"); + /* Set the name of our kerberos ticket file */ + krb_set_tkt_string("/tmp/tkt_ureg"); + + /* Connect to the Moira server */ + if ((status = mr_connect(MOIRA_SERVER)) != MR_SUCCESS) + { + com_err(whoami, status, " on mr_connect"); exit(1); } - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket"); + + /* Authorize, telling the server who you are */ + if ((status = mr_auth(whoami)) != MR_SUCCESS) + { + com_err(whoami, status, " on mr_auth"); exit(1); } - bzero((char *)&sin, sizeof(sin)); - - sin.sin_family = AF_INET; - sin.sin_port = sp->s_port; - sin.sin_addr.s_addr = INADDR_ANY; - - if (bind(s, &sin, sizeof(sin)) < 0) { - perror("bind"); + + journal = fopen(REGJOURNAL, "a"); + if (journal == NULL) { + com_err(whoami, errno, " while opening journal file"); exit(1); } - for (;;) { - addrlen = sizeof(sin); - bzero(retval, BUFSIZ); - len = recvfrom(s, buf, BUFSIZ, 0, &sin, &addrlen); - if (len < 0) { - perror("recvfrom"); - if (errno == EINTR) continue; - - exit(1); - } - /* Parse a request packet */ - status = parse_pkt(buf, len, &seqno, &message); - if (status != 0) { - len = BUFSIZ; - format_pkt(buf, &len, seqno, status, (char *)NULL); - (void) sendto(s, buf, len, 0, &sin, addrlen); - continue; - } - /* do action */ - switch((int)message.request) { - case UREG_VERIFY_USER: - status = verify_user(&message); + /* Allow request layer to initialize */ + req_initialize(); + + /* Sit around waiting for requests from the client. */ + for (;;) + { + get_request(&message); + + switch((int)message.request) + { + case UREG_VERIFY_USER: + status = verify_user(&message,retval); + break; + case UREG_RESERVE_LOGIN: + status = reserve_user(&message,retval); break; - case UREG_RESERVE_LOGIN: - status = reserve_user(&message); + case UREG_SET_PASSWORD: + case UREG_GET_KRB: + status = set_password(&message,retval); break; - case UREG_SET_PASSWORD: - status = set_password(&message); + case UREG_SET_IDENT: + status = set_identity(&message,retval); break; - - default: + case UREG_GET_SECURE: + status = get_secure(&message,retval); + break; + case UREG_SET_SECURE: + status = set_secure(&message,retval); + break; + default: status = UREG_UNKNOWN_REQUEST; + critical_alert(FAIL_INST,"Unknown request %d from userreg.", + message.request); break; } - len = BUFSIZ; - format_pkt(buf, &len, seqno, status, retval); - sendto(s, buf, len, 0, &sin, addrlen); + /* Report what happened to client */ + report(status, retval); } } -int got_one; -int reg_status; -char *mit_id; -char *reg_misc; -int reg_misc_len; -int user_id; - -#define min(a,b) ((a)>(b)?(b):(a)) - -int validate_idno(message, db_mit_id, first, last) - struct msg *message; - char *db_mit_id; - char *first, *last; +int parse_encrypted(message,data) + struct msg *message; /* Formatted packet */ + struct db_data *data; /* Data from the Moira database */ +/* This routine makes sure that the ID from the database matches + the ID sent accross in the packet. The information in the packet + was created in the following way: + + The database used to contain encrypted IDs. Now we don't encrypt + them in the database, although there are still some encrypted IDs + there. + + The plain text ID number was encrypted via EncryptID() resulting + in the form that would appear in the Moira database. This is + concatinated to the plain text ID so that the ID string contains plain + text ID followed by a null followed by the encrypted ID. Other + information such as the username or password is appended. The whole + thing is then DES encrypted using the encrypted ID as the source of + the key. + + This routine tries each ID in the database that belongs + to someone with this user's first and last name and tries to + decrypt the packet with this information. If it succeeds, it returns + zero and initializes all the fields of the formatted packet structure + that depend on the encrypted information. */ { - C_Block key; - Key_schedule sched; - static char decrypt[BUFSIZ]; - char recrypt[14]; - static char hashid[14]; - char idnumber[BUFSIZ]; - char *temp; - int len; - - int i; -#ifdef notdef - for (i = 0; i < message->sealed_len; i++) { - printf("%02x ", (unsigned char)message->sealed[i]); - } - printf("\n"); -#endif notdef - mit_id = 0; + des_cblock key; /* The key for DES en/decryption */ + des_key_schedule sched; /* En/decryption schedule */ + static char decrypt[BUFSIZ]; /* Buffer to hold decrypted information */ + long decrypt_len; /* Length of decypted ID information */ + char recrypt[14]; /* Buffer to hold re-encrypted information */ + static char hashid[14]; /* Buffer to hold one-way encrypted ID */ + char idnumber[BUFSIZ]; /* Buffer to hold plain-text ID */ + char *temp; /* A temporary string pointer */ + int len; /* Keeps track of length left in packet */ + int status = SUCCESS; /* Error status */ - string_to_key(db_mit_id, key); - key_sched(key, sched); - pcbc_encrypt(message->sealed, decrypt, message->sealed_len, sched, key, 0); - -#ifdef notdef - for (i = 0; i < message->sealed_len; i++) { - printf("%02x ", (unsigned char)decrypt[i]); - } - printf("\n"); - for (i = 0; i < message->sealed_len; i++) { - if (isprint(decrypt[i])) - printf("%c ", (unsigned char)decrypt[i]); - else printf(". "); - } - printf("\n"); -#endif notdef - (void) strncpy(idnumber, decrypt, message->sealed_len); - temp = decrypt + strlen(idnumber) + 1; - len = message->sealed_len - (temp - decrypt); +#ifdef DEBUG + com_err(whoami, 0, "Entering parse_encrypted"); +#endif + + /* Make the decrypted information length the same as the encrypted + information length. Both are integral multples of eight bytes + because of the DES encryption routines. */ + decrypt_len = (long)message->encrypted_len; + + /* Get key from the possibly one-way encrypted ID in the Moira database */ + if (data->mit_id[0] >= '0' && data->mit_id[0] <= '9') { + char buf[32]; + + EncryptID(buf, data->mit_id, message->first, message->last); + des_string_to_key(buf, key); + } else + des_string_to_key(data->mit_id, key); + + /* Get schedule from key */ + des_key_sched(key, sched); + /* Decrypt information from packet using this key. Since decrypt_len + is an integral multiple of eight bytes, it will probably be null- + padded. */ + des_pcbc_encrypt(message->encrypted, decrypt, decrypt_len, + sched, key, DES_DECRYPT); - (void) strncpy(hashid, temp, min(len, 14)); + /* Extract the plain text and encrypted ID fields from the decrypted + packet information. */ + /* Since the decrypted information starts with the plain-text ID + followed by a null, if the decryption worked, this will only + copy the plain text part of the decrypted information. It is + important that strncpy be used because if we are not using the + correct key, there is no guarantee that a null will occur + anywhere in the string. */ + (void) strncpy(idnumber,decrypt,(int)decrypt_len); + /* Check that the idnumber of a mismatched decryption doesn't overflow + * the buffer. + */ + if (strlen(idnumber) != 9) { +#ifdef DEBUG + com_err(whoami, 0, "idnumber wrong size, probable user mismatch\n"); +#endif + return(FAILURE); + } + /* Point temp to the end of the plain text ID number. */ + temp = decrypt + strlen(idnumber) + 1; + /* Find out how much more packet there is. */ + len = message->encrypted_len - (temp - decrypt); + /* Copy the next CRYPT_LEN bytes of the decrypted information into + hashid if there are CRYPT_LEN more bytes to copy. There will be + if we have the right key. */ + (void) strncpy(hashid, temp, min(len, CRYPT_LEN)); + /* Point temp to the end of the encrypted ID field */ temp += strlen(hashid) + 1; - len = message->sealed_len - (temp - decrypt); - - if (strcmp(hashid, db_mit_id)) return 1; - encrypt_mitid(recrypt, idnumber, first, last); - if (strcmp(recrypt, db_mit_id)) return 1; - - reg_misc = temp; - reg_misc_len = len; - mit_id = hashid; - return 0; -} - -static int status_in_db; - -vfy_callbk(argc, argv, p_message) - int argc; /* Should sanity check this.. */ - char **argv; - char *p_message; -{ - struct msg *message = (struct msg *)p_message; - char *db_mit_id; - char *firstname, *lastname; - int status; + /* Find out how much more room there is. */ + len = message->encrypted_len - (temp - decrypt); -#ifdef debug - printf("Callback: %s %s %s\n", argv[8], argv[5], argv[4]); -#endif debug - if (got_one) return 0; - reg_status = 0; + /* Now compare encrypted ID and clear text ID for a match. */ + if (strcmp(hashid, data->mit_id) && + strcmp(idnumber, data->mit_id)) + status = FAILURE; - db_mit_id = argv[8]; - firstname = argv[5]; - lastname = argv[4]; - - status = validate_idno(message, db_mit_id, firstname, lastname); - if (status) return 0; /* Nope; decryption failed */ - - status_in_db = atoi(argv[7]); - reg_status = status_in_db; - - if (status_in_db != 0) { - (void) strcpy(retval, argv[0]); + if (status == SUCCESS) + { + /* We made it. Now we can finish initializing message. */ + /* Point leftover to whatever is left over! */ + message->leftover = temp; + message->leftover_len = len; + /* Since we know we have the right user, fill in the information + from the Moira database. */ + message->db.reg_status = data->reg_status; + (void) strncpy(message->db.uid,data->uid, sizeof(message->db.uid)); + (void) strncpy(message->db.mit_id,data->mit_id, + sizeof(message->db.mit_id)); + (void) strncpy(message->db.login,data->login, sizeof(message->db.login)); } - user_id = atoi(argv[1]); - got_one = 1; - return 0; -} - -encrypt_mitid(buf, idnumber, first, last) - char *buf, *idnumber, *first, *last; -{ - char salt[3]; - extern char *crypt(); -#define _tolower(c) ((c)|0x60) +#ifdef DEBUG + if (status) + com_err(whoami, status, " in parse_encrypted"); + else + com_err(whoami, status, "parse_encrypted succeeded"); +#endif - salt[0] = _tolower(last[0]); - salt[1] = _tolower(first[0]); - salt[2] = 0; - - (void) strcpy(buf, crypt(&idnumber[2], salt)); + return status; } -int verify_user(message) - struct msg *message; - +int db_callproc(argc,argv,queue) + int argc; /* Number of arguments returned by Moira */ + char *argv[]; /* Arguments returned by Moira */ + struct save_queue *queue; /* Queue to save information in */ +/* This function is called by mr_query after each tuple found. It is + used by find_user to cache information about each user found. */ { - char *argv[3]; - int status; - - printf("verify_user\n"); - argv[0] = "get_user_by_first_and_last"; - argv[1] = message->first; - argv[2] = message->last; - got_one = 0; + struct db_data *data; /* Structure to store the information in */ + int status = SUCCESS; /* Error status */ - status = sms_query_internal(3, argv, vfy_callbk, (char *)message); +#ifdef DEBUG + com_err(whoami, 0, "Entering db_callproc."); +#endif + + if (argc != U_END) + { + critical_alert + (FAIL_INST, + "Wrong number of arguments returned from get_user_account_by_name."); + status = MR_ABORT; + } + else + { + /* extract the needed information from the results of the Moira query */ + data = (struct db_data *)malloc(sizeof(struct db_data)); + data->reg_status = atoi(argv[U_STATE]); + (void) strncpy(data->login,argv[U_NAME],sizeof(data->login)); + (void) strncpy(data->mit_id,argv[U_MITID],sizeof(data->mit_id)); + (void) strncpy(data->uid,argv[U_UID],sizeof(data->uid)); +#ifdef DEBUG + fprintf(stderr,"Found in database:\n"); + fprintf(stderr," Registration status: %d\n",data->reg_status); + fprintf(stderr," login: %s\n",data->login); + fprintf(stderr," MIT ID: %s\n",data->mit_id); + fprintf(stderr," uid: %s\n",data->uid); +#endif + sq_save_data(queue,data); + } + + return status; +} - if (status == SMS_NO_MATCH) status = UREG_USER_NOT_FOUND; - if (!got_one && !status) - status = UREG_USER_NOT_FOUND; +int find_user(message) + struct msg *message; /* Formatted packet structure */ +/* This routine verifies that a user is allowed to register by finding + him/her in the Moira database. It returns the status of the Moira + query that it calls. */ +{ +#define GUBN_ARGS 2 /* Arguements needed by get_user_by_name */ + char *q_name; /* Name of query */ + int q_argc; /* Number of arguments for query */ + char *q_argv[GUBN_ARGS]; /* Arguments to query */ + int status = SUCCESS; /* Query return status */ + + struct save_queue *queue; /* Queue to hold Moira data */ + struct db_data *data; /* Structure for data for one tuple */ + short verified = FALSE; /* Have we verified the user? */ + + /* Zero the mit_id field in the formatted packet structure. This + being zeroed means that no user was found. */ + bzero(message->db.mit_id,sizeof(message->db.mit_id)); - if (status != 0) goto punt; + if (status == SUCCESS) + { + /* Get ready to make an Moira query */ + q_name = "get_user_account_by_name"; + q_argc = GUBN_ARGS; /* #defined in this routine */ + q_argv[0] = message->first; + q_argv[1] = message->last; + + /* Create queue to hold information */ + queue = sq_create(); + + /* Do it */ + status = mr_query(q_name,q_argc,q_argv,db_callproc,(char *)queue); + +#ifdef DEBUG + fprintf(stderr," %d returned by get_user_by_name\n",status); +#endif + + if (status == MR_SUCCESS) + { + /* Traverse the list, freeing data as we go. If sq_get_data() + returns zero if there is no more data on the queue. */ + while (sq_get_data(queue,&data)) + { + if (!verified) + /* parse_encrypted returns zero on success */ + verified = (parse_encrypted(message,data) == SUCCESS); + free((char *)data); + } + } - if (reg_status == 1) status = UREG_ALREADY_REGISTERED; - if (reg_status == 2) status = UREG_NO_PASSWD_YET; + /* Destroy the queue */ + sq_destroy(queue); + } -punt: +#ifdef DEBUG + fprintf(stderr,"Returned from find_user\n"); + fprintf(stderr," MIT ID: %s\n", message->db.mit_id); + fprintf(stderr," Registration status: %d\n",message->db.reg_status); + fprintf(stderr," uid: %s\n",message->db.uid); + fprintf(stderr," login: %s\n",message->db.login); + fprintf(stderr," Status from query: %d\n",status); +#endif DEBGUG + return status; } -reserve_user(message) - struct msg *message; +int verify_user(message,retval) + struct msg *message; + char *retval; + /* This routine determines whether a user is in the databse and returns + his state so that other routines can figure out whether he is the + correct state for various transactions. */ + { - char *argv[3]; - int status; - int i; - char *login; - char uid_buf[20]; - - STAMP; - printf("reserve_user\n"); + int status = SUCCESS; /* Return status */ - argv[0] = "gufl"; /* get_user_by_first_and_last */ - argv[1] = message->first; - argv[2] = message->last; - got_one = 0; - - status = sms_query_internal(3, argv, vfy_callbk, (char *)message); + /* Log that we are about to veryify user */ + com_err(whoami, 0, "verifying user %s %s",message->first,message->last); - STAMP; - - if (status == SMS_NO_MATCH) status = UREG_USER_NOT_FOUND; - if (!got_one && !status) - status = UREG_USER_NOT_FOUND; + /* Figure out what user (if any) can be found based on the + encrypted information in the packet. (See the comment on + parse_encrypted().) */ - if (status != 0) goto punt; - if (reg_status != 0) { - status = UREG_ALREADY_REGISTERED; - goto punt; - } - /* - * He's made it past this phase already. - */ - if (status_in_db == 2) { - status = 0; - goto punt; - } - /* Sanity check requested login name. */ - printf("reg_misc_len = %d\n", reg_misc_len); + status = find_user(message); - for (i = 0; i < reg_misc_len && reg_misc[i]; i++) { - if (!islower(reg_misc[i])) { - status = UREG_INVALID_UNAME; - goto punt; + /* If Moira coudn't find the user */ + if (status == MR_NO_MATCH) + status = UREG_USER_NOT_FOUND; + else if (status == MR_SUCCESS) + { + /* If the information sent over in the packet did not point to a + valid user, the mit_id field in the formatted packet structure + will be empty. */ + if (message->db.mit_id[0] == NULL) + status = UREG_USER_NOT_FOUND; + /* If the user was found but the registration has already started, + use this as the status */ + else + { + switch (message->db.reg_status) + { + case US_NO_LOGIN_YET: + status = SUCCESS; + break; + case US_REGISTERED: + status = UREG_ALREADY_REGISTERED; + break; + case US_NO_PASSWD: + status = UREG_NO_PASSWD_YET; + break; + case US_DELETED: + status = UREG_DELETED; + break; + case US_NOT_ALLOWED: + status = UREG_NOT_ALLOWED; + break; + case US_ENROLLED: + status = UREG_ENROLLED; + break; + case US_ENROLL_NOT_ALLOWED: + status = UREG_ENROLL_NOT_ALLOWED; + break; + case US_HALF_ENROLLED: + status = UREG_HALF_ENROLLED; + break; + default: + status = UREG_MISC_ERROR; + critical_alert(FAIL_INST,"Bad user state %d for login %s.", + message->db.reg_status, message->db.login); + break; + } + /* Set retval to the login name so that the client can use + it in the error message it will give the user. */ + (void) strcpy(retval,message->db.login); } } - if (i < 3 || i > 8) { - status = UREG_INVALID_UNAME; - goto punt; - } - login = reg_misc; - - /* Send request to kerberos admin_server for login name */ - /* get keys */ - printf("get_svc_in_tkt\n"); - status = get_svc_in_tkt("register", "kerberos", "ATHENA.MIT.EDU", - "changepw", "kerberos", - 1, "/etc/srvtab"); - if (status) { - status += krb_err_base; - goto punt; - } - STAMP; - - printf("admin_call\n"); - /* send set password request to kerberos admin_server */ - (void) sprintf(uid_buf, "%013d", user_id); /* 13 chars of placebo */ - /* for backwards-compat. */ - status = admin_call(ADMIN_ADD_NEW_KEY_ATTR, login, "", - "", uid_buf); + if (status) + com_err(whoami, status, " returned from verify_user"); + else + com_err(whoami, 0, "User verified"); - if (status) { - if (status == ADMIN_SERVER_ERROR) { - printf("Server error: %s\n", admin_errmsg); - - if (strcmp(admin_errmsg, - "Principal already in kerberos database.") ==0) - status = UREG_LOGIN_USED; - } - goto punt; + return status; +} + +int ureg_kadm_init() +{ + unsigned int status = SUCCESS; /* Return status */ + static char krbrealm[REALM_SZ]; /* kerberos realm name */ + static char hostbuf[BUFSIZ], *host; /* local hostname in principal fmt */ + static int inited = 0; + char *p; + +#ifdef DEBUG + com_err(whoami, 0, "Entering ureg_kadm_init"); +#endif DEBUG + + if (!inited) { + inited++; + bzero(krbrealm, sizeof(krbrealm)); + if (status = krb_get_lrealm(krbrealm, 1)) { + status += krb_err_base; + com_err(whoami, status, " fetching kerberos realm"); + exit(1); + } + if (gethostname(hostbuf, sizeof(hostbuf)) < 0) + com_err(whoami, errno, "getting local hostname"); + host = canonicalize_hostname(strsave(hostbuf)); + for (p = host; *p && *p != '.'; p++) + if (isupper(*p)) + *p = tolower(*p); + *p = 0; } - dest_tkt(); - /* If valid: */ - STAMP; - - /* Set login name */ - status = set_login(login, mit_id); - if (status) { - com_err("set_login", status, 0); - goto punt; - } - /* choose post office */ - STAMP; - - status = choose_pobox(login); - if (status) { - com_err("choose_pobox", status, 0); - goto punt; - } - /* set quota entry, create filsys */ - STAMP; - - status = alloc_filsys(login, SMS_FS_STUDENT, 0, 0); - if (status) { - com_err("alloc_filsys", status, 0); - goto punt; - } - /* create group */ - STAMP; + /* Get keys for interacting with Kerberos admin server. */ + /* principal, instance, realm, service, service instance, life, file */ + if (status = krb_get_svc_in_tkt(MOIRA_SNAME, host, krbrealm, PWSERV_NAME, + KADM_SINST, 1, KEYFILE)) + status += krb_err_base; - status = create_group(login); - if (status) { - com_err("create_group", status, 0); - goto punt; + if (status != SUCCESS) + com_err(whoami, status, " while get admin tickets"); +#ifdef DEBUG + else { + com_err(whoami, status, "Succeeded in getting admin tickets"); } - /* set filsys and status in SMS database */ - STAMP; - - status = set_status_filsys(reg_misc, mit_id); - if (status) { - com_err("set_filsys", status, 0); - goto punt; +#endif + + if (status == SUCCESS) { + if ((status = kadm_init_link(PWSERV_NAME, KADM_SINST, krbrealm)) != + KADM_SUCCESS) { + com_err(whoami, status, " while initializing kadmin connection"); + } } -punt: - dest_tkt(); - STAMP; - printf("reserve_user returning %s\n", error_message(status)); + return status; } -set_password(message) - struct msg *message; +int null_callproc(argc,argv,message) + int argc; + char *argv[]; + char *message; + /* This routine is a null callback that should be used for queries that + do not return tuples. If it ever gets called, something is wrong. */ { - char *argv[3]; - int status; - - printf("set_password\n"); + critical_alert(FAIL_INST,"Something returned from an update query."); + return FAILURE; +} - /* validate that user is who he claims to be */ +/* + * This routine reserves a principal in kerberos by setting up a + * principal with a random initial key. + */ +int reserve_krb(login) + char *login; +{ + int status = SUCCESS; + Kadm_vals new; + des_cblock key; + u_long *lkey = (u_long *)key; + +#ifdef DEBUG + com_err(whoami, 0, "Entering reserve_krb"); +#endif DEBUG + + if ((status = ureg_kadm_init()) == SUCCESS) { + bzero((char *)&new, sizeof(new)); + SET_FIELD(KADM_DESKEY, new.fields); + SET_FIELD(KADM_NAME, new.fields); + + (void) des_random_key(key); + new.key_low = htonl(lkey[0]); + new.key_high = htonl(lkey[1]); + strcpy(new.name, login); + + com_err(whoami, 0, "Creating kerberos principal for %s", login); + status = kadm_add(&new); + if (status != KADM_SUCCESS) + com_err(whoami, status, " while reserving principal"); + + bzero((char *)&new, sizeof(new)); + } - argv[0] = "get_user_by_first_and_last"; - argv[1] = message->first; - argv[2] = message->last; - got_one = 0; - - status = sms_query_internal(3, argv, vfy_callbk, (char *)message); - - if (status == SMS_NO_MATCH) status = UREG_USER_NOT_FOUND; - if (!got_one && !status) - status = UREG_USER_NOT_FOUND; - - if (status != 0) goto punt; + dest_tkt(); - /* validate that state is equal to '2' (login, but no password) */ + return(status); +} - if (reg_status != 2) { - status = UREG_NO_LOGIN_YET; - goto punt; +/* + * This routine reserves a principal in kerberos by setting up a + * principal with a random initial key. + */ +int setpass_krb(login, password) + char *login; + char *password; +{ + int status = SUCCESS; + Kadm_vals new; + des_cblock key; + u_long *lkey = (u_long *)key; + + if ((status = ureg_kadm_init()) == SUCCESS) { + bzero((char *)&new, sizeof(new)); + SET_FIELD(KADM_DESKEY, new.fields); + SET_FIELD(KADM_NAME, new.fields); + + (void) des_string_to_key(password, key); + new.key_low = htonl(lkey[0]); + new.key_high = htonl(lkey[1]); + strcpy(new.name, login); + + com_err(whoami, 0, "Setting password for %s", login); + /* First arguement is not used if user has modify privileges */ + if ((status = kadm_mod(&new, &new)) != KADM_SUCCESS) { + if (status == KADM_NOENTRY) { + com_err(whoami, 0, + "kerberos principal doesn't exist; creating"); + if ((status = kadm_add(&new)) != KADM_SUCCESS) + com_err(whoami, status, + " while creating kerberos principal"); + } + else + com_err(whoami, status, " while setting password"); + } } + + dest_tkt(); + return(status); +} - printf("password for %s would be set to %s\n", retval ,reg_misc); +int reserve_user(message,retval) + struct msg *message; + char *retval; +{ + int q_argc; /* Number of arguments to query */ + char *q_argv[3]; /* Arguments to Moira query */ + char *q_name; /* Name of Moira query */ + int status = SUCCESS; /* General purpose error status */ + char fstype_buf[7]; /* Buffer to hold fs_type, a 16 bit number */ + char *login; /* The login name the user wants */ + register int i; /* A counter */ + + /* Log that we are about to reserve a user. */ + com_err(whoami, 0, "reserving user %s %s", + message->first, message->last); + + /* Check to make sure that we can verify this user. */ + if ((status = verify_user(message,retval)) == SUCCESS) + { + /* Get the requested login name from leftover packet information. */ + login = message->leftover; + + /* Check the login name for validity. The login name is currently + is allowed to contain lowercase letters in any position and + and numbers and underscore characters in any position but the + first. */ + if ((strlen(login) < MIN_UNAME) || (strlen(login) > MAX_UNAME)) + status = UREG_INVALID_UNAME; + } + if (status == SUCCESS) + if ((login[0] == '_') || isdigit(login[0])) + status = UREG_INVALID_UNAME; + + if (status == SUCCESS) + { + for (i = 0; i < strlen(login); i++) + if (!islower(login[i]) && !isdigit(login[i]) && + (login[i] != '_')) + { + status = UREG_INVALID_UNAME; + break; + } + } + if (status == SUCCESS) + { + /* Now that we have a valid user with a valid login... */ + + /* First, try to reserve the user in Moira. */ + (void) sprintf(fstype_buf,"%d",MR_FS_STUDENT); + q_name = "register_user"; + q_argv[0] = message->db.uid; + q_argv[1] = login; + q_argv[2] = fstype_buf; + q_argc = 3; + status = mr_query(q_name,q_argc,q_argv,null_callproc,(char *)0); + switch (status) + { + case MR_SUCCESS: + status = SUCCESS; + break; + case MR_IN_USE: + status = UREG_LOGIN_USED; + break; + case MR_DEADLOCK: + status = UREG_MISC_ERROR; + break; + default: + critical_alert(FAIL_INST,"%s returned from register_user.", + error_message(status)); + status = UREG_MISC_ERROR; + break; + } + } - /* get keys */ - status = get_svc_in_tkt("register", "kerberos", "ATHENA.MIT.EDU", - "changepw", "kerberos", - 1, "/etc/srvtab"); - if (status) { - status += krb_err_base; - goto punt; + if (status == SUCCESS) + { + /* + * Moira login was successfully created; try to reserve kerberos + * principal. + * + * If this routine fails, store the login in the retval so + * that it can be used in the client-side error message. + */ + if ((status = reserve_krb(login)) != SUCCESS) + (void) strcpy(retval, login); } - /* send set password request to kerberos admin_server */ - status = admin_call(ADMIN_SET_KDC_PASSWORD, retval, "", - reg_misc, "BBBBBBBBBBBBB"); - if (status) goto punt; - dest_tkt(); + if (status) + com_err(whoami, status, " returned from reserve_user"); + else { + com_err(whoami, 0, "User reserved"); + } - status = set_final_status(retval, mit_id); - - /* reflect reply to client */ -punt: - dest_tkt(); return status; } -parse_pkt(buf, len, seqnop, messagep) - char *buf; - int len; - u_long *seqnop; - struct msg *messagep; +int set_final_status(message) +struct msg *message; + /* This routine updates a user's registration status to fully + registered. */ { - if (len < 4) return UREG_BROKEN_PACKET; - bcopy(buf, (char *)&messagep->version, sizeof(long)); - messagep->version = ntohl(messagep->version); - if (messagep->version != 1) return UREG_WRONG_VERSION; - - buf += 4; - len -= 4; + char *login; + char *q_name; /* Name of Moira query */ + int q_argc; /* Number of arguments for Moira query */ + char *q_argv[2]; /* Arguments to get user by uid */ + char state[7]; /* Can hold a 16 bit integer */ + int status; /* Error status */ + + if (message->request == UREG_SET_PASSWORD) + (void) sprintf(state,"%d",US_REGISTERED); + else if (message->db.reg_status == US_NO_LOGIN_YET) + (void) sprintf(state,"%d",US_ENROLLED); + else + (void) sprintf(state,"%d",US_ENROLL_NOT_ALLOWED); + + login = message->db.login; + com_err(whoami, 0, "Setting final status for %s to %s", login, state); + + q_name = "update_user_status"; + q_argc = 2; + q_argv[0] = login; + q_argv[1] = state; + if ((status = mr_query(q_name, q_argc, q_argv, null_callproc, + (char *)0)) != MR_SUCCESS) { + if (status == MR_DEADLOCK) + status = UREG_MISC_ERROR; + else + critical_alert(FAIL_INST,"%s returned from update_user_status.", + error_message(status)); + } + if (status) + com_err(whoami, status, " returned from set_final_status"); + else + com_err(whoami, 0, "Final status set"); + return status; +} - if (len < 4) return UREG_BROKEN_PACKET; - bcopy(buf, (char *)seqnop, sizeof(long)); - buf += 4; - len -= 4; - - if (len < 4) return UREG_BROKEN_PACKET; - bcopy(buf, (char *)(&messagep->request), sizeof(long)); - messagep->request = ntohl(messagep->request); - buf += 4; - len -= 4; - - messagep->first = buf; +int set_password(message,retval) + struct msg *message; + char *retval; + /* This routine is used to set the initial password for the new user. */ +{ + int status = SUCCESS; /* Return status */ + char *passwd; /* User's password */ - for (; *buf && len > 0; --len, ++buf) continue; - if (len <= 0) return UREG_BROKEN_PACKET; + com_err(whoami, 0, "setting password %s %s", + message->first, message->last); - buf++, len--; + status = verify_user(message,retval); - messagep->last = buf; + /* Don't set the password unless the registration status of the user + is that he exists and has no password. */ + if (status == SUCCESS) + status = UREG_NO_LOGIN_YET; + if (((int)message->request == UREG_SET_PASSWORD && + status == UREG_NO_PASSWD_YET) || + ((int)message->request == UREG_GET_KRB && + status == UREG_HALF_ENROLLED)) + { + /* User is in proper state for this transaction. */ + + passwd = message->leftover; + + /* Set password. */ + if ((status = setpass_krb(message->db.login, passwd)) != SUCCESS) + /* If failure, allow login name to be used in client + error message */ + (void) strcpy(retval,message->db.login); + else + /* Otherwise, mark user as finished. */ + status = set_final_status(message); + } - for (; *buf && len > 0; --len, ++buf) continue; - if (len <= 0) return UREG_BROKEN_PACKET; - - buf++, len--; + if (status) + com_err(whoami, status, " returned from set_passwd"); + else + com_err(whoami, 0, "Password set"); - if (len <= 0) return UREG_BROKEN_PACKET; - - messagep->sealed = buf; - messagep->sealed_len = len; - - return 0; + return status; } -format_pkt(buf, lenp, seqno, status, message) - char *buf; - int *lenp; - u_long seqno; - int status; - char *message; + +int getuserinfo(argc, argv, qargv) +int argc; +char **argv; +char **qargv; { - u_long vers = htonl((u_long)1); - status = htonl((u_long)status); - - bcopy((char *)&vers, buf, sizeof(long)); - bcopy((char *)&seqno, buf+sizeof(long), sizeof(long)); - bcopy((char *)&status, buf+ 2*sizeof(long), sizeof(long)); - *lenp = sizeof(long) * 3; - (void) strcpy(buf+3*sizeof(long), message); - (*lenp) += strlen(message); + int status = SUCCESS; + int i; + + if (argc != U_END) { + critical_alert(FAIL_INST, + "Wrong number of args returned from get_user_by_uid"); + status = MR_ABORT; + } else { + qargv[U_NAME] = strsave(argv[U_NAME]); + for (i = 1; i < U_MODTIME; i++) + qargv[i+1] = strsave(argv[i]); + qargv[U_MODTIME+1] = NULL; + } + return(status); } -store_user(argc, argv, argp) - int argc; - char **argv; - char *argp; + +int set_identity(message,retval) +struct msg *message; +char *retval; { - char **retv = (char **) argp; - int i; + int q_argc; /* Number of arguments to query */ + char *q_argv[U_END]; /* Arguments to Moira query */ + char *q_name; /* Name of Moira query */ + int status = SUCCESS; /* General purpose error status */ + char fstype_buf[7]; /* Buffer to hold fs_type, a 16 bit number */ + char *login; /* The login name the user wants */ + register int i; /* A counter */ + + /* Log that we are about to reserve a user. */ + com_err(whoami, 0, "setting identity %s %s", + message->first, message->last); - for (i = 0; i < argc; i++) { - if (retv[i]) { - free(retv[i]); - retv[i]=0; + /* Check to make sure that we can verify this user. */ + status = verify_user(message,retval); + if (status == SUCCESS || status == UREG_NOT_ALLOWED) + { + status = SUCCESS; + /* Get the requested login name from leftover packet information. */ + login = message->leftover; + + /* Check the login name for validity. The login name is currently + is allowed to contain lowercase letters in any position and + and numbers and underscore characters in any position but the + first. */ + if ((strlen(login) < MIN_UNAME) || (strlen(login) > MAX_UNAME)) + status = UREG_INVALID_UNAME; + } + if (status == SUCCESS) + if ((login[0] == '_') || isdigit(login[0])) + status = UREG_INVALID_UNAME; + if (status == SUCCESS) + { + for (i = 0; i < strlen(login); i++) + if (!islower(login[i]) && !isdigit(login[i]) && + (login[i] != '_')) + { + status = UREG_INVALID_UNAME; + break; + } + } + if (status == SUCCESS) + { + /* Now that we have a valid user with a valid login... */ + + q_argv[0] = message->db.uid; + status = mr_query("get_user_account_by_uid", 1, q_argv, + getuserinfo, q_argv); + if (status != SUCCESS) { + com_err(whoami, status, " while getting user info"); + return(status); + } + q_argv[U_NAME+1] = login; + q_argv[U_STATE+1] = "7"; + q_argv[U_SIGNATURE+1] = ""; + status = mr_query("update_user_account", U_MODTIME+1, q_argv, + null_callproc, NULL); + switch (status) + { + case MR_SUCCESS: + status = SUCCESS; + break; + case MR_IN_USE: + case MR_NOT_UNIQUE: + status = UREG_LOGIN_USED; + break; + case MR_DEADLOCK: + status = UREG_MISC_ERROR; + break; + default: + critical_alert(FAIL_INST,"%s returned from update_user_account.", + error_message(status)); + status = UREG_MISC_ERROR; + break; } - retv[i] = strdup(argv[i]); } - return 0; -} + if (status == SUCCESS) + { + /* Moira login was successfully created; try to reserve kerberos + principal. */ + /* If this routine fails, store the login in the retval so + that it can be used in the client-side error message. */ + if ((status = reserve_krb(login)) != SUCCESS) + (void) strcpy(retval, login); + } + + if (status) + com_err(whoami, status, " returned from set_identity"); + else + com_err(whoami, 0, "Identity set"); + return status; +} -/* - * Set login name of user with "idnumber" to be "username" - */ -set_login(username, idnumber) - char *username; - char *idnumber; +void reg_com_err_hook(whoami, code, fmt, pvar) + char *whoami; + int code; + char *fmt; + caddr_t pvar; { - char *argv[2]; - int status, i; - char *retv[13]; - - argv[0] = "get_user_by_mitid"; - argv[1] = idnumber; + if (whoami) { + fputs(whoami, stderr); + fputs(": ", stderr); + } + if (code) { + fputs(error_message(code), stderr); + } + if (fmt) { + _doprnt(fmt, pvar, stderr); + } + putc('\n', stderr); + fflush(stderr); +} - for (i=0; i<13; i++) { - retv[i] = 0; - } - status = sms_query_internal(2, argv, store_user, (char *)(retv+1)); - if (status) return status; +/* Find out of someone's secure instance password is set. + * Returns UREG_ALREADY_REGISTERED if set, SUCCESS (0) if not. + */ - retv[0] = retv[1]; - retv[1] = username; - if (retv[4]) free(retv[4]); - retv[4] = "null"; /* No such filesystem */ - - printf("Update_user(%s, %s)\n", retv[0], retv[1]); - - status = sms_query("update_user", 12, retv, abort, 0); - retv[1] = 0; - retv[4] = 0; - - for (i=1; i<12; i++) { - if (retv[i]) free(retv[i]); - retv[i] = 0; +int get_secure(message, retval) +struct msg *message; +char *retval; +{ + int status; + char *argv[U_END]; + + com_err(whoami, 0, "checking status of secure password for %s", + message->first); + argv[0] = message->first; + status = mr_query("get_user_account_by_login", 1, argv, getuserinfo, argv); + if (status != SUCCESS) { + com_err(whoami, status, " while getting user info"); + return(status); } - - return status; -} + if (atoi(argv[U_SECURE + 1])) + return UREG_ALREADY_REGISTERED; + return SUCCESS; +} -/* - * Set the status and filsys of user with username "uname" and filesys filsys. - */ -set_status_filsys(username, idnumber) - char *username; - char *idnumber; +/* Set someone's secure instance password. */ + +int set_secure(message, retval) +struct msg *message; +char *retval; { - char *argv[2]; int status, i; - char *retv[13]; - - argv[0] = "get_user_by_mitid"; - argv[1] = idnumber; - - for (i=0; i<13; i++) { - retv[i] = 0; + char *argv[U_END], hostbuf[256], *bp, *p, buf[512], *passwd, *id; + KTEXT_ST creds; + AUTH_DAT auth; + C_Block key; + Key_schedule keys; + Kadm_vals kv; + u_long *lkey = (u_long *)key; + struct timeval now; + static int inited = 0; + static char *host; + + if (!inited) { + inited++; + if (gethostname(hostbuf, sizeof(hostbuf)) < 0) + com_err(whoami, errno, "getting local hostname"); + host = strsave(krb_get_phost(hostbuf)); } - status = sms_query_internal(2, argv, store_user, (char *)(retv+1)); - if (status) return status; - - retv[0] = retv[1]; - - free(retv[4]); - retv[4] = username; + com_err(whoami, 0, "setting secure passwd for %s", message->first); + argv[0] = message->first; + status = mr_query("get_user_account_by_login", 1, argv, getuserinfo, argv); + if (status != SUCCESS) { + com_err(whoami, status, " while getting user info"); + return(status); + } + if (atoi(argv[U_SECURE + 1])) { + com_err(whoami, UREG_ALREADY_REGISTERED, "in set_secure()"); + return UREG_ALREADY_REGISTERED; + } - free(retv[8]); - retv[8] = "2"; + bp = message->encrypted; + /* round up to word boundary */ + bp = (char *)((((u_long)bp + 3) >> 2) << 2); - printf("Update_user(%s, %s)\n", retv[0], retv[1]); + creds.length = ntohl(*((int *)bp)); + bp += sizeof(int); + bcopy(bp, creds.dat, creds.length); + creds.mbz = 0; + bp += creds.length; + +#ifdef DEBUG + com_err(whoami, 0, "Cred: length %d", creds.length); + for (i = 0; i < creds.length; i += 16) + com_err(whoami, 0, " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + creds.dat[i+0], creds.dat[i+1], creds.dat[i+2], creds.dat[i+3], + creds.dat[i+4], creds.dat[i+5], creds.dat[i+6], creds.dat[i+7], + creds.dat[i+8], creds.dat[i+9], creds.dat[i+10], creds.dat[i+11], + creds.dat[i+12], creds.dat[i+13], creds.dat[i+14], creds.dat[i+15]); +#endif /* DEBUG */ - status = sms_query("update_user", 12, retv, abort, 0); - retv[4] = 0; - retv[8] = 0; - for (i=1; i<12; i++) { - if (retv[i]) free(retv[i]); - retv[i] = 0; + status = krb_rd_req(&creds, "changepw", host, cur_req_sender(), + &auth, ""); + if (status) { + status += krb_err_base; + com_err(whoami, status, " verifying credentials in set_secure()"); + return(status); } - return status; -} -/* - * Set the status and filsys of user with username "uname" and filesys filsys. - */ -set_final_status(username, idnumber) - char *username; - char *idnumber; -{ - char *argv[2]; - int status, i; - char *retv[13]; - - argv[0] = "get_user_by_mitid"; - argv[1] = idnumber; - - for (i=0; i<13; i++) { - retv[i] = 0; + message->leftover_len = ntohl(*((int*)(bp))); + bp += sizeof(int); + message->leftover = bp; + + des_key_sched(auth.session, keys); + des_pcbc_encrypt(message->leftover, buf, message->leftover_len, + keys, auth.session, 0); + + id = buf; + passwd = index(buf, ','); + *passwd++ = 0; +#ifdef DEBUG + com_err(whoami, 0, "Got id: %s, passwd %d chars", id, strlen(passwd)); +#endif + + if (strcmp(id, argv[U_MITID + 1])) { + char buf[32]; + + EncryptID(buf, id, argv[U_FIRST+1], argv[U_LAST+1]); + if (strcmp(buf, argv[U_MITID + 1])) { + status = UREG_USER_NOT_FOUND; + com_err(whoami, status, "IDs mismatch: %s (%s), %s", id, buf, + argv[U_MITID + 1]); + return status; + } } - status = sms_query_internal(2, argv, store_user, (char *)(retv+1)); - if (status) return status; - - retv[0] = retv[1]; + /* now do actual password setting stuff */ - free(retv[8]); - retv[8] = "1"; - - printf("Update_user(%s, %s)\n", retv[0], retv[1]); - - status = sms_query("update_user", 12, retv, abort, 0); - retv[8] = 0; - for (i=1; i<12; i++) { - if (retv[i]) free(retv[i]); - retv[i] = 0; + if ((status = ureg_kadm_init()) != SUCCESS) { + com_err(whoami, status, "initing kadm stuff"); + return(status); } - return status; -} -create_group(login) - char *login; -{ - int status; - static char *cr[] = { - "add_user_group", - 0, - }; - - cr[1] = login; - - return sms_query_internal(2, cr, abort, 0); -} -/* - * Local Variables: - * mode: c - * c-indent-level: 4 - * c-continued-statement-offset: 4 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * End: - */ -char *get_krbhst(a1, a2) - char *a1; - char *a2; -{ - strcpy(a1, "ICARUS.MIT.EDU"); - return 0; -} + bzero((char *)&kv, sizeof(kv)); + SET_FIELD(KADM_DESKEY, kv.fields); + SET_FIELD(KADM_NAME, kv.fields); + SET_FIELD(KADM_INST, kv.fields); + (void) des_string_to_key(passwd, key); + kv.key_low = htonl(lkey[0]); + kv.key_high = htonl(lkey[1]); + strcpy(kv.name, message->first); + strcpy(kv.instance, "extra"); + + if ((status = kadm_add(&kv)) != KADM_SUCCESS) { + com_err(whoami, status, " while creating kerberos principal"); + return(status); + } -char *get_krbrlm(a1) - char *a1; -{ - strcpy(a1, "ATHENA.MIT.EDU"); - return 0; + argv[0] = message->first; + argv[1] = buf; + gettimeofday(&now, NULL); + sprintf(buf, "%d", now.tv_sec); + status = mr_query("update_user_security_status", 2, argv, getuserinfo, argv); + if (status != SUCCESS) { + com_err(whoami, status, " while updating user status"); + return(status); + } + return SUCCESS; } -