6 * Copyright (C) 1987 by the Massachusetts Institute of Technology
8 * Server for user registration with SMS and Kerberos.
10 * This program is a client of the SMS server and the Kerberos
11 * admin_server, and is a server for the userreg program.
14 * Revision 1.9 1988-07-26 14:50:40 qjb
15 * Added comments and did some cleaning up in preparation for rewrite.
16 * This version will not run; the last version that will is 1.8.
18 * Revision 1.8 88/07/20 15:39:25 mar
19 * find realm at runtime; don't use hard-coded one
21 * Revision 1.7 88/02/08 15:08:15 mar
22 * Moved header file locations
24 * Revision 1.6 87/09/21 15:19:11 wesommer
25 * Allow numbers, _, and . as legal characters in the username.
27 * Revision 1.5 87/09/10 22:18:32 wesommer
28 * Clean up output format.
30 * Revision 1.4 87/09/04 23:33:19 wesommer
31 * Deleted test scaffolding (second oops.)
33 * Revision 1.3 87/09/03 03:05:18 wesommer
34 * Version used for userreg tests.
36 * Revision 1.2 87/08/22 18:39:45 wesommer
37 * User registration server.
39 * Revision 1.1 87/07/31 15:48:13 wesommer
45 static char *rcsid_reg_svr_c = "$Header$";
49 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
59 #include "ureg_proto.h"
61 #include "admin_server.h"
62 #include "admin_err.h"
65 #define WHOAMI "reg_svr" /* Name of program for SMS logging */
66 #define CUR_SMS_VERSION SMS_VERSION_2 /* SMS version for this program */
70 extern char *strdup();
71 extern char *malloc();
72 extern int krb_err_base;
73 extern char admin_errmsg[];
75 extern int errno; /* Unix error number */
77 /* This is the structure used to store the packet information */
78 /* Get this. The register client gets the MIT id of the registering user
79 in plain text, encrypts it with one-way password encryption,
80 concatenates that to the plain text id, and then des encrypts the
81 whole thing using the password encrypted id as the key! The result
85 u_long version; /* SMS version */
86 u_long request; /* Request */
87 char *first; /* First name */
88 char *last; /* Last name */
89 char *enc_mitid; /* See comment above */
90 int enc_mitid_len; /* Length of enc_mitid */
93 static char errmsg[BUFSIZ];
97 struct servent *sp; /* Service info from /etc/services */
98 int s; /* Socket descriptor */
99 struct sockaddr_in sin; /* Internet style socket address */
100 int addrlen; /* Size of socket address (sin) */
101 char packet[BUFSIZ]; /* Buffer for packet transmission */
102 int pktlen; /* Size of packet */
103 u_long seqno; /* Sequence number for packet transmission */
104 struct msg message; /* Storage for parsed packet */
105 int status; /* General purpose error status */
107 /* Error messages sent one line at a time */
110 /* Initialize user registration error table */
113 /* Get service information from /etc/services */
114 if ((sp = getservbyname("sms_ureg", "udp")) == NULL)
116 fprintf(stderr, "Unknown service sms_ureg/udp\n");
120 /* Get an internet style datagram socket */
121 if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
126 bzero((char *)&sin, sizeof(sin));
128 sin.sin_family = AF_INET;
129 sin.sin_port = sp->s_port;
130 sin.sin_addr.s_addr = INADDR_ANY;
132 /* Bind a name to the socket */
133 if (bind(s, &sin, sizeof(sin)) < 0)
139 /* Connect to the SMS server */
140 if ((status = sms_connect()) != SMS_SUCCESS)
142 com_err(WHOAMI, status, " on connect");
146 /* Authorize, telling the server who you are */
147 if ((status = sms_auth(WHOAMI)) != SMS_SUCCESS)
149 com_err(WHOAMI, status, " on auth");
153 /* Sit around waiting for requests from the client. */
156 com_err(WHOAMI, 0, "Ready for next request");
157 addrlen = sizeof(sin);
158 bzero(errmsg, BUFSIZ);
159 /* Receive a packet into buf. */
160 if ((pktlen = recvfrom(s,packet,sizeof(packet),0,&sin,&addrlen)) < 0)
163 if (errno == EINTR) continue;
167 /* Parse a request packet */
168 if ((status = parse_pkt(packet, pktlen, &seqno, &message)) != 0)
170 pktlen = sizeof(packet);
171 /* Format packet to send back to the client */
172 format_pkt(packet, &pktlen, seqno, status, (char *)NULL);
173 /* Report the error the the client */
174 (void) sendto(s, packet, pktlen, 0, &sin, addrlen);
179 switch((int)message.request)
181 case UREG_VERIFY_USER:
182 status = verify_user(&message);
184 case UREG_RESERVE_LOGIN:
185 status = reserve_user(&message);
187 case UREG_SET_PASSWORD:
188 status = set_password(&message);
192 status = UREG_UNKNOWN_REQUEST;
196 /* Report what happened to client */
197 pktlen = sizeof(packet);
198 format_pkt(packet, &pktlen, seqno, status, errmsg);
199 sendto(s, packet, pktlen, 0, &sin, addrlen);
210 #define min(a,b) ((a)>(b)?(b):(a))
212 int validate_idno(message, db_mit_id, first, last)
213 struct msg *message; /* Formatted packet */
214 char *db_mit_id; /* Encrypted MIT ID from SMS database */
215 char *first, *last; /* First and last name for MIT ID encryption */
216 /* This routine makes sure that the ID from the database matches
217 the ID sent accross in the packet. The information in the packet
218 was created in the following way:
220 The plain text ID number was encrypted via crypt() resulting in
221 the form that would appear in the SMS database. This is
222 concatinated to the plain text ID so that the ID string contains
223 plain text ID followed by a null followed by the encrypted ID.
224 The whole thing is then DES encrypted using the encrypted ID as
225 the source of the key.
227 This routine tries each encrypted ID in the database that belongs
228 to someone with this user's first and last name and tries to
229 decrypt the packet with this information. */
231 C_Block key; /* The key for DES en/decryption */
232 Key_schedule sched; /* En/decryption schedule */
233 static char decrypt[BUFSIZ]; /* Buffer to hold decrypted information */
234 long decrypt_len; /* Length of decypted ID information */
235 char recrypt[14]; /* Buffer to hold re-encrypted information */
236 static char hashid[14]; /* Buffer to hold one-way encrypted ID */
237 char idnumber[BUFSIZ]; /* Buffer to hold plain-text ID */
238 char *temp; /* A temporary storage buffer */
242 /* Make the decrypted information length the same as the encrypted
243 information length. Both are integral multples of eight bytes
244 because of the DES encryption routines. */
245 decrypt_len = (long)message->enc_mitid_len;
247 /* Get key from the one-way encrypted ID in the SMS database */
248 string_to_key(db_mit_id, key);
249 /* Get schedule from key */
250 key_sched(key, sched);
251 /* Decrypt information from packet using this key. Since decrypt_len
252 is an integral multiple of eight bytes, it will probably be null-
254 pcbc_encrypt(message->enc_mitid, decrypt, \
255 decrypt_len, sched, key, DECRYPT);
257 /* Extract the plain text and encrypted ID fields from the decrypted
258 packet information. */
259 /* Since the decrypted information starts with the plain-text ID
260 followed by a null, if the decryption worked, this will only
261 copy the plain text part of the decrypted information. It is
262 important that strncpy be used because if we are not using the
263 correct key, there is no guarantee that a null will occur
264 anywhere in the string. */
265 (void) strncpy(idnumber, decrypt, decrypt_len);
266 /* Point temp to the end of the plain text ID number. */
267 temp = decrypt + strlen(idnumber) + 1;
268 /* Find out how much more room there is. */
269 len = message->enc_mitid_len - (temp - decrypt);
270 /* Copy the next 14 bytes of the decrypted information into
271 hashid if there are 14 more bytes to copy. There will be
272 if we have the right key. */
273 (void) strncpy(hashid, temp, min(len, 14));
274 /* Point temp to the end of the encrypted ID field */
275 temp += strlen(hashid) + 1;
276 /* Find out how much more room there is. */
277 len = message->enc_mitid_len - (temp - decrypt);
279 /* Now compare encrypted ID's returning with an error if they
281 if (strcmp(hashid, db_mit_id)) return 1;
282 encrypt_mitid(recrypt, idnumber, first, last);
283 /* Now compare encrypted plain text to ID from database. */
284 if (strcmp(recrypt, db_mit_id)) return 1;
293 static int status_in_db;
295 vfy_callbk(argc, argv, p_message)
296 int argc; /* Should sanity check this.. */
300 struct msg *message = (struct msg *)p_message;
302 char *firstname, *lastname;
305 if (got_one) return 0;
312 status = validate_idno(message, db_mit_id, firstname, lastname);
313 if (status) return 0; /* Nope; decryption failed */
315 status_in_db = atoi(argv[7]);
316 reg_status = status_in_db;
318 if (status_in_db != 0)
320 (void) strcpy(errmsg, argv[0]);
322 user_id = atoi(argv[1]);
327 encrypt_mitid(buf, idnumber, first, last)
328 char *buf, *idnumber, *first, *last;
331 extern char *crypt();
333 #define _tolower(c) ((c)|0x60)
335 salt[0] = _tolower(last[0]);
336 salt[1] = _tolower(first[0]);
339 (void) strcpy(buf, crypt(&idnumber[2], salt));
342 int verify_user(message)
348 com_err(WHOAMI, 0, " verify_user %s %s\n",
349 message->first, message->last);
350 argv[0] = "get_user_by_first_and_last";
351 argv[1] = message->first;
352 argv[2] = message->last;
355 status = sms_query_internal(3, argv, vfy_callbk, (char *)message);
357 if (status == SMS_NO_MATCH) status = UREG_USER_NOT_FOUND;
358 if (!got_one && !status)
359 status = UREG_USER_NOT_FOUND;
361 if (status != SMS_SUCCESS) goto punt;
363 if (reg_status == 1) status = UREG_ALREADY_REGISTERED;
364 if (reg_status == 2) status = UREG_NO_PASSWD_YET;
370 reserve_user(message)
378 char realm[REALM_SZ];
380 com_err(WHOAMI, 0, " reserve_user %s %s\n",
381 message->first, message->last);
383 argv[0] = "gufl"; /* get_user_by_first_and_last */
384 argv[1] = message->first;
385 argv[2] = message->last;
388 status = sms_query_internal(3, argv, vfy_callbk, (char *)message);
390 if (status == SMS_NO_MATCH) status = UREG_USER_NOT_FOUND;
391 if (!got_one && !status)
392 status = UREG_USER_NOT_FOUND;
394 if (status != SMS_SUCCESS) goto punt;
397 status = UREG_ALREADY_REGISTERED;
401 * He's made it past this phase already.
403 if (status_in_db == 2)
409 for (i = 0; i < reg_misc_len && reg_misc[i]; i++)
411 if (!islower(reg_misc[i]) && !isdigit(reg_misc[i]) &&
412 reg_misc[i] != '_' && reg_misc[i] != '.')
414 status = UREG_INVALID_UNAME;
420 status = UREG_INVALID_UNAME;
425 /* Send request to kerberos admin_server for login name */
427 if ((status = get_krbrlm(realm, 1)) != KSUCCESS)
429 status += krb_err_base;
432 status = get_svc_in_tkt("register", "sms", realm,
433 "changepw", "kerberos",
437 status += krb_err_base;
441 /* send set password request to kerberos admin_server */
442 (void) sprintf(uid_buf, "%013d", user_id); /* 13 chars of placebo */
443 /* for backwards-compat. */
445 status = admin_call(ADMIN_ADD_NEW_KEY_ATTR, login, "",
450 if (status == ADMIN_SERVER_ERROR)
452 printf("Server error: %s\n", admin_errmsg);
454 if (strcmp(admin_errmsg,
455 "Principal already in kerberos database.") ==0)
456 status = UREG_LOGIN_USED;
465 status = set_login(login, mit_id);
470 com_err("set_login", status, (char *)0);
473 /* choose post office */
475 status = choose_pobox(login);
478 com_err("choose_pobox", status, (char *)0);
483 status = create_group(login);
484 if (status == SMS_LIST) status = UREG_LOGIN_USED;
488 com_err("create_group", status, (char *)0);
491 /* set quota entry, create filsys */
493 status = alloc_filsys(login, SMS_FS_STUDENT, 0, 0);
494 if (status == SMS_FILESYS_EXISTS) status = UREG_LOGIN_USED;
497 com_err("alloc_filsys", status, (char *)0);
500 /* set filsys and status in SMS database */
502 status = set_status_filsys(reg_misc, mit_id);
505 com_err("set_filsys", status, (char *)0);
511 com_err(WHOAMI, status, " returned from reserve_user");
515 set_password(message)
521 char realm[REALM_SZ];
523 com_err(WHOAMI, 0, " set_password %s %s\n",
524 message->first, message->last);
526 /* validate that user is who he claims to be */
528 argv[0] = "get_user_by_first_and_last";
529 argv[1] = message->first;
530 argv[2] = message->last;
533 status = sms_query_internal(3, argv, vfy_callbk, (char *)message);
535 if (status == SMS_NO_MATCH) status = UREG_USER_NOT_FOUND;
536 if (!got_one && !status)
537 status = UREG_USER_NOT_FOUND;
539 if (status != SMS_SUCCESS) goto punt;
541 /* validate that state is equal to '2' (login, but no password) */
545 status = UREG_NO_LOGIN_YET;
550 if ((status = get_krbrlm(realm, 1)) != KSUCCESS)
554 status = get_svc_in_tkt("register", "sms", realm,
555 "changepw", "kerberos",
559 status += krb_err_base;
563 (void) sprintf(uid_buf, "%013d", user_id); /* 13 chars of placebo */
564 /* for backwards-compat. */
565 /* send set password request to kerberos admin_server */
567 status = admin_call(ADMIN_ADD_NEW_KEY_ATTR, errmsg, "",
570 if (status) goto punt;
573 status = set_final_status(errmsg, mit_id);
575 /* reflect reply to client */
581 parse_pkt(packet, pktlen, seqnop, messagep)
585 struct msg *messagep;
586 /* This routine checks a packet and puts the information in it in
587 a structure if it is valid. */
589 if (pktlen < 4) return UREG_BROKEN_PACKET;
590 /* Extract the SMS version from the packet */
591 bcopy(packet, (char *)&messagep->version, sizeof(long));
592 /* Convert byte order from network to host */
593 messagep->version = ntohl(messagep->version);
595 if (messagep->version != CUR_SMS_VERSION) return UREG_WRONG_VERSION;
600 if (pktlen < 4) return UREG_BROKEN_PACKET;
601 /* Extract the sequence number from the packet */
602 bcopy(packet, (char *)seqnop, sizeof(long));
607 if (pktlen < 4) return UREG_BROKEN_PACKET;
608 /* Extract the request from the packet */
609 bcopy(packet, (char *)(&messagep->request), sizeof(long));
610 messagep->request = ntohl(messagep->request);
614 /* Extract first name from the packet */
615 messagep->first = packet;
617 /* Scan forward until null appears in the packet or there
618 is no more packet! */
619 for (; *packet && pktlen > 0; --pktlen, ++packet) continue;
620 if (pktlen <= 0) return UREG_BROKEN_PACKET;
622 /* Skip over the null */
625 /* Extract last name from the packet */
626 messagep->last = packet;
628 for (; *packet && pktlen > 0; --pktlen, ++packet) continue;
629 if (pktlen <= 0) return UREG_BROKEN_PACKET;
633 if (pktlen <= 0) return UREG_BROKEN_PACKET;
635 /* Extract MIT id information from packet; see comment on
637 messagep->enc_mitid = packet;
638 messagep->enc_mitid_len = pktlen;
643 format_pkt(packet, pktlenp, seqno, status, message)
649 /* This routine prepares a packet to send back to the client. */
651 u_long vers = htonl((u_long)CUR_SMS_VERSION);
652 status = htonl((u_long)status);
654 /* Put current SMS version into the packet */
655 bcopy((char *)&vers, packet, sizeof(long));
656 /* Put sequence number into the packet */
657 bcopy((char *)&seqno, packet+sizeof(long), sizeof(long));
658 /* Put error status into the packet */
659 bcopy((char *)&status, packet+ 2*sizeof(long), sizeof(long));
660 *pktlenp = sizeof(long) * 3;
661 /* Copy the message into the packet */
662 (void) strcpy(packet+3*sizeof(long), message);
663 (*pktlenp) += strlen(message);
666 store_user(argc, argv, argp)
671 char **retv = (char **) argp;
674 for (i = 0; i < argc; i++)
681 retv[i] = strdup(argv[i]);
688 * Set login name of user with "idnumber" to be "username"
691 set_login(username, idnumber)
699 argv[0] = "get_user_by_mitid";
707 status = sms_query_internal(2, argv, store_user, (char *)(retv+1));
708 if (status) return status;
712 if (retv[4]) free(retv[4]);
713 retv[4] = "null"; /* No such filesystem */
715 printf("Update_user(%s, %s)\n", retv[0], retv[1]);
717 status = sms_query("update_user", 12, retv, abort, 0);
723 if (retv[i]) free(retv[i]);
731 * Set the status and filsys of user with username "uname" and filesys filsys.
734 set_status_filsys(username, idnumber)
742 argv[0] = "get_user_by_mitid";
750 status = sms_query_internal(2, argv, store_user, (char *)(retv+1));
751 if (status) return status;
761 printf("Update_user(%s, %s)\n", retv[0], retv[1]);
763 status = sms_query("update_user", 12, retv, abort, 0);
768 if (retv[i]) free(retv[i]);
774 * Set the status and filsys of user with username "uname" and filesys filsys.
777 set_final_status(username, idnumber)
785 argv[0] = "get_user_by_mitid";
793 status = sms_query_internal(2, argv, store_user, (char *)(retv+1));
794 if (status) return status;
801 printf("Update_user(%s, %s)\n", retv[0], retv[1]);
803 status = sms_query("update_user", 12, retv, abort, 0);
807 if (retv[i]) free(retv[i]);
825 return sms_query_internal(2, cr, abort, 0);
831 * c-argdecl-indent: 2
833 * c-continued-statement-offset: 2