]> andersk Git - moira.git/blame_incremental - reg_svr/reg_svr.c
mentioned extra non-GECOS info
[moira.git] / reg_svr / reg_svr.c
... / ...
CommitLineData
1/*
2 * $Source$
3 * $Author$
4 * $Header$
5 *
6 * Copyright (C) 1987, 1988 by the Massachusetts Institute of Technology
7 * For copying and distribution information, please see the file
8 * <mit-copyright.h>.
9 *
10 * Server for user registration with SMS and Kerberos.
11 *
12 * This program is a client of the Kerberos admin_server and a
13 * server for the userreg program. It is not a client of the
14 * SMS server as it is linked with libsmsglue which bypasses
15 * the network protocol.
16 */
17
18#ifndef lint
19static char *rcsid_reg_svr_c = "$Header$";
20#endif lint
21
22#include <mit-copyright.h>
23#include "reg_svr.h"
24#include "admin_server.h"
25#include "admin_err.h"
26
27extern int krb_err_base;
28extern char admin_errmsg[];
29
30static char krbhst[BUFSIZ]; /* kerberos server name */
31static char krbrealm[REALM_SZ]; /* kerberos realm name */
32
33main(argc,argv)
34 int argc;
35 char *argv[];
36{
37 struct msg message; /* Storage for parsed packet */
38 int status = SUCCESS; /* Error status */
39 char retval[BUFSIZ]; /* Buffer to hold return message for client */
40
41 void req_initialize(); /* Initialize request layer */
42 void get_request(); /* Get a request */
43 void report(); /* Respond to a request */
44
45 /* Initialize */
46 whoami = argv[0];
47
48 /* Use com_err or output to stderr for all log messages. */
49#ifdef DEBUG
50 fprintf(stderr,"*** Debugging messages enabled. ***\n");
51#endif DEBUG
52
53 /* Error messages sent one line at a time */
54 setlinebuf(stderr);
55
56 /* Initialize user registration error table for com_err */
57 init_ureg_err_tbl();
58
59 /* Connect to the SMS server */
60 if ((status = sms_connect(SMS_SERVER)) != SMS_SUCCESS)
61 {
62 com_err(whoami, status, " on connect");
63 exit(1);
64 }
65
66 /* Authorize, telling the server who you are */
67 if ((status = sms_auth(whoami)) != SMS_SUCCESS)
68 {
69 com_err(whoami, status, " on auth");
70 exit(1);
71 }
72
73 if (status = get_krbrlm(krbrealm, 1)) {
74 status += krb_err_base;
75 com_err(whoami, status, " fetching kerberos realm");
76 exit(1);
77 }
78
79 if (status = get_krbhst(krbhst, krbrealm, 1)) {
80 status += krb_err_base;
81 com_err(whoami, status, " fetching kerberos hostname");
82 exit(1);
83 } else {
84 char *s;
85 for (s = krbhst; *s && *s != '.'; s++)
86 if (isupper(*s))
87 *s = tolower(*s);
88 *s = 0;
89 }
90
91 journal = fopen(JOURNAL, "a");
92 if (journal == NULL) {
93 com_err(whoami, errno, " while opening journal file");
94 exit(1);
95 }
96
97 /* Allow request layer to initialize */
98 req_initialize();
99
100 /* Sit around waiting for requests from the client. */
101 for (;;)
102 {
103 get_request(&message);
104
105 switch((int)message.request)
106 {
107 case UREG_VERIFY_USER:
108 status = verify_user(&message,retval);
109 break;
110 case UREG_RESERVE_LOGIN:
111 status = reserve_user(&message,retval);
112 break;
113 case UREG_SET_PASSWORD:
114 status = set_password(&message,retval);
115 break;
116
117 default:
118 status = UREG_UNKNOWN_REQUEST;
119 critical_alert(FAIL_INST,"Unknown request %d from userreg.",
120 message.request);
121 break;
122 }
123
124 /* Report what happened to client */
125 report(status, retval);
126 }
127}
128
129/* This is necessary so that this server can know where to put its
130 tickets. */
131char *tkt_string()
132{
133 return("/tmp/tkt_ureg");
134}
135
136int parse_encrypted(message,data)
137 struct msg *message; /* Formatted packet */
138 struct db_data *data; /* Data from the SMS database */
139/* This routine makes sure that the ID from the database matches
140 the ID sent accross in the packet. The information in the packet
141 was created in the following way:
142
143 The plain text ID number was encrypted via EncryptID() resulting
144 in the form that would appear in the SMS database. This is
145 concatinated to the plain text ID so that the ID string contains plain
146 text ID followed by a null followed by the encrypted ID. Other
147 information such as the username or password is appended. The whole
148 thing is then DES encrypted using the encrypted ID as the source of
149 the key.
150
151 This routine tries each encrypted ID in the database that belongs
152 to someone with this user's first and last name and tries to
153 decrypt the packet with this information. If it succeeds, it returns
154 zero and initializes all the fields of the formatted packet structure
155 that depend on the encrypted information. */
156{
157 C_Block key; /* The key for DES en/decryption */
158 Key_schedule sched; /* En/decryption schedule */
159 static char decrypt[BUFSIZ]; /* Buffer to hold decrypted information */
160 long decrypt_len; /* Length of decypted ID information */
161 char recrypt[14]; /* Buffer to hold re-encrypted information */
162 static char hashid[14]; /* Buffer to hold one-way encrypted ID */
163 char idnumber[BUFSIZ]; /* Buffer to hold plain-text ID */
164 char *temp; /* A temporary string pointer */
165 int len; /* Keeps track of length left in packet */
166 int status = SUCCESS; /* Error status */
167
168#ifdef DEBUG
169 com_err(whoami,0,"Entering parse_encrypted");
170#endif
171
172 /* Make the decrypted information length the same as the encrypted
173 information length. Both are integral multples of eight bytes
174 because of the DES encryption routines. */
175 decrypt_len = (long)message->encrypted_len;
176
177 /* Get key from the one-way encrypted ID in the SMS database */
178 string_to_key(data->mit_id, key);
179 /* Get schedule from key */
180 key_sched(key, sched);
181 /* Decrypt information from packet using this key. Since decrypt_len
182 is an integral multiple of eight bytes, it will probably be null-
183 padded. */
184 pcbc_encrypt(message->encrypted,decrypt, decrypt_len, sched, key, DECRYPT);
185
186 /* Extract the plain text and encrypted ID fields from the decrypted
187 packet information. */
188 /* Since the decrypted information starts with the plain-text ID
189 followed by a null, if the decryption worked, this will only
190 copy the plain text part of the decrypted information. It is
191 important that strncpy be used because if we are not using the
192 correct key, there is no guarantee that a null will occur
193 anywhere in the string. */
194 (void) strncpy(idnumber,decrypt,(int)decrypt_len);
195 /* Point temp to the end of the plain text ID number. */
196 temp = decrypt + strlen(idnumber) + 1;
197 /* Find out how much more packet there is. */
198 len = message->encrypted_len - (temp - decrypt);
199 /* Copy the next CRYPT_LEN bytes of the decrypted information into
200 hashid if there are CRYPT_LEN more bytes to copy. There will be
201 if we have the right key. */
202 (void) strncpy(hashid, temp, min(len, CRYPT_LEN));
203 /* Point temp to the end of the encrypted ID field */
204 temp += strlen(hashid) + 1;
205 /* Find out how much more room there is. */
206 len = message->encrypted_len - (temp - decrypt);
207
208 /* Now compare encrypted ID's don't match. */
209 if (strcmp(hashid, data->mit_id)) status = FAILURE;
210 if (status == SUCCESS)
211 {
212 EncryptID(recrypt, idnumber, message->first, message->last);
213 /* Now compare encrypted plain text to ID from database. */
214 if (strcmp(recrypt, data->mit_id)) status = FAILURE;
215 }
216
217 if (status == SUCCESS)
218 {
219 /* We made it. Now we can finish initializing message. */
220 /* Point leftover to whatever is left over! */
221 message->leftover = temp;
222 message->leftover_len = len;
223 /* Since we know we have the right user, fill in the information
224 from the SMS database. */
225 message->db.reg_status = data->reg_status;
226 (void) strncpy(message->db.uid,data->uid, sizeof(message->db.uid));
227 (void) strncpy(message->db.mit_id,data->mit_id,
228 sizeof(message->db.mit_id));
229 (void) strncpy(message->db.login,data->login, sizeof(message->db.login));
230 }
231
232#ifdef DEBUG
233 if (status)
234 com_err(whoami,status," parse_encrypted failed.");
235 else
236 com_err(whoami,status,"parse_encrypted succeeded.");
237#endif
238
239 return status;
240}
241
242int db_callproc(argc,argv,queue)
243 int argc; /* Number of arguments returned by SMS */
244 char *argv[]; /* Arguments returned by SMS */
245 struct save_queue *queue; /* Queue to save information in */
246/* This function is called by sms_query after each tuple found. It is
247 used by find_user to cache information about each user found. */
248{
249 struct db_data *data; /* Structure to store the information in */
250 int status = SUCCESS; /* Error status */
251
252#ifdef DEBUG
253 com_err(whoami,0,"Entering db_callproc.");
254#endif
255
256 if (argc != U_END)
257 {
258 critical_alert
259 (FAIL_INST,
260 "Wrong number of arguments returned from get_user_by_name.");
261 status = SMS_ABORT;
262 }
263 else
264 {
265 /* extract the needed information from the results of the SMS query */
266 data = (struct db_data *)malloc(sizeof(struct db_data));
267 data->reg_status = atoi(argv[U_STATE]);
268 (void) strncpy(data->login,argv[U_NAME],sizeof(data->login));
269 (void) strncpy(data->mit_id,argv[U_MITID],sizeof(data->mit_id));
270 (void) strncpy(data->uid,argv[U_UID],sizeof(data->uid));
271#ifdef DEBUG
272 fprintf(stderr,"Found in database:\n");
273 fprintf(stderr," Registration status: %d\n",data->reg_status);
274 fprintf(stderr," login: %s\n",data->login);
275 fprintf(stderr," MIT ID: %s\n",data->mit_id);
276 fprintf(stderr," uid: %s\n",data->uid);
277#endif
278 sq_save_data(queue,data);
279 }
280
281 return status;
282}
283
284int find_user(message)
285 struct msg *message; /* Formatted packet structure */
286/* This routine verifies that a user is allowed to register by finding
287 him/her in the SMS database. It returns the status of the SMS
288 query that it calls. */
289{
290#define GUBN_ARGS 2 /* Arguements needed by get_user_by_name */
291 char *q_name; /* Name of query */
292 int q_argc; /* Number of arguments for query */
293 char *q_argv[GUBN_ARGS]; /* Arguments to query */
294 int status = SUCCESS; /* Query return status */
295
296 struct save_queue *queue; /* Queue to hold SMS data */
297 struct db_data *data; /* Structure for data for one tuple */
298 short verified = FALSE; /* Have we verified the user? */
299
300 /* Zero the mit_id field in the formatted packet structure. This
301 being zeroed means that no user was found. */
302 bzero(message->db.mit_id,sizeof(message->db.mit_id));
303
304 if (status == SUCCESS)
305 {
306 /* Get ready to make an SMS query */
307 q_name = "get_user_by_name";
308 q_argc = GUBN_ARGS; /* #defined in this routine */
309 q_argv[0] = message->first;
310 q_argv[1] = message->last;
311
312 /* Create queue to hold information */
313 queue = sq_create();
314
315 /* Do it */
316 status = sms_query(q_name,q_argc,q_argv,db_callproc,(char *)queue);
317
318#ifdef DEBUG
319 fprintf(stderr," %d returned by get_user_by_name\n",status);
320#endif
321
322 if (status == SMS_SUCCESS)
323 {
324 /* Traverse the list, freeing data as we go. If sq_get_data()
325 returns zero if there is no more data on the queue. */
326 while (sq_get_data(queue,&data))
327 {
328 if (!verified)
329 /* parse_encrypted returns zero on success */
330 verified = (parse_encrypted(message,data) == SUCCESS);
331 free((char *)data);
332 }
333 }
334
335 /* Destroy the queue */
336 sq_destroy(queue);
337 }
338
339#ifdef DEBUG
340 fprintf(stderr,"Returned from find_user\n");
341 fprintf(stderr," MIT ID: %s\n", message->db.mit_id);
342 fprintf(stderr," Registration status: %d\n",message->db.reg_status);
343 fprintf(stderr," uid: %s\n",message->db.uid);
344 fprintf(stderr," login: %s\n",message->db.login);
345 fprintf(stderr," Status from query: %d\n",status);
346#endif DEBGUG
347
348 return status;
349}
350
351int verify_user(message,retval)
352 struct msg *message;
353 char *retval;
354 /* This routine determines whether a user is in the databse and returns
355 his state so that other routines can figure out whether he is the
356 correct state for various transactions. */
357
358{
359 int status = SUCCESS; /* Return status */
360
361 /* Log that we are about to veryify user */
362 com_err(whoami,0,"verify_user %s %s",message->first,message->last);
363
364 /* Figure out what user (if any) can be found based on the
365 encrypted information in the packet. (See the comment on
366 parse_encrypted().) */
367
368 status = find_user(message);
369
370 /* If SMS coudn't find the user */
371 if (status == SMS_NO_MATCH)
372 status = UREG_USER_NOT_FOUND;
373 else if (status == SMS_SUCCESS)
374 {
375 /* If the information sent over in the packet did not point to a
376 valid user, the mit_id field in the formatted packet structure
377 will be empty. */
378 if (message->db.mit_id[0] == NULL)
379 status = UREG_USER_NOT_FOUND;
380 /* If the user was found but the registration has already started,
381 use this as the status */
382 else
383 {
384 switch (message->db.reg_status)
385 {
386 case US_NO_LOGIN_YET:
387 status = SUCCESS;
388 break;
389 case US_REGISTERED:
390 status = UREG_ALREADY_REGISTERED;
391 break;
392 case US_NO_PASSWD:
393 status = UREG_NO_PASSWD_YET;
394 break;
395 case US_DELETED:
396 status = UREG_DELETED;
397 break;
398 case US_NOT_ALLOWED:
399 status = UREG_NOT_ALLOWED;
400 break;
401
402 default:
403 status = UREG_MISC_ERROR;
404 critical_alert(FAIL_INST,"Bad user state for login %s.",
405 message->db.login);
406 break;
407 }
408 /* Set retval to the login name so that the client can use
409 it in the error message it will give the user. */
410 (void) strcpy(retval,message->db.login);
411 }
412 }
413
414 com_err(whoami,status," returned from verify_user");
415
416 return status;
417}
418
419int ureg_get_tkt()
420{
421 int status = SUCCESS; /* Return status */
422
423 /* Get keys for interacting with Kerberos admin server. */
424 /* principal, instance, realm, service, service instance, life, file */
425 if (status = get_svc_in_tkt("register", "sms", krbrealm, "changepw",
426 krbhst, 1, KEYFILE))
427 status += krb_err_base;
428
429#ifdef DEBUG
430 if (status == SUCCESS)
431 com_err(whoami,status,"Succeeded in getting tickets.");
432 else
433 com_err(whoami,status,"Failed to get tickets.");
434#endif
435 return status;
436}
437
438int null_callproc(argc,argv,message)
439 int argc;
440 char *argv[];
441 char *message;
442 /* This routine is a null callback that should be used for queries that
443 do not return tuples. If it ever gets called, something is wrong. */
444{
445 critical_alert(FAIL_INST,"Something returned from an update query.");
446 return FAILURE;
447}
448
449int do_admin_call(login, passwd, uid)
450 char *login; /* Requested kerberos principal */
451 char *passwd; /* Requested password */
452 char *uid; /* Uid of user who owns this principal */
453 /* This routine gets tickets, makes the appropriate call to admin_call,
454 and destroys tickets. */
455{
456 int status; /* Error status */
457 char uid_buf[20]; /* Holds uid for kerberos */
458
459 com_err(whoami,0,"Entering do_admin_call");
460
461 if ((status = ureg_get_tkt()) == SUCCESS)
462 {
463 /* Try to reserve kerberos principal. To do this, send a
464 password request and a null password. It will only succeed
465 if there is no principal or the principal exists and has no
466 password. */
467 /* 13 chars of placebo for backwards-compatability - the admin
468 server protocol reqires this. */
469 bzero(uid_buf,sizeof(uid_buf));
470 (void) sprintf(uid_buf, "%13s", uid);
471
472 if ((status = admin_call(ADMIN_ADD_NEW_KEY_ATTR, login,
473 "", passwd, uid_buf)) != KSUCCESS)
474 {
475 com_err(whoami,status," server error: %s",admin_errmsg);
476
477 if (strcmp(admin_errmsg,
478 "Principal already in kerberos database.") == 0)
479 status = UREG_KRB_TAKEN;
480 critical_alert(FAIL_INST,"%s is known to Kerberos but not SMS.",
481 login);
482 }
483 }
484
485 dest_tkt();
486 com_err(whoami,status," returned from do_adin_call");
487 return status;
488}
489
490int reserve_user(message,retval)
491 struct msg *message;
492 char *retval;
493{
494 int q_argc; /* Number of arguments to query */
495 char *q_argv[3]; /* Arguments to SMS query */
496 char *q_name; /* Name of SMS query */
497 int status = SUCCESS; /* General purpose error status */
498 char fstype_buf[7]; /* Buffer to hold fs_type, a 16 bit number */
499 char *login; /* The login name the user wants */
500 register int i; /* A counter */
501
502 /* Log that we are about to reserve a user. */
503 com_err(whoami, 0, "reserve_user %s %s",
504 message->first, message->last);
505
506 /* Check to make sure that we can verify this user. */
507 if ((status = verify_user(message,retval)) == SUCCESS)
508 {
509 /* Get the requested login name from leftover packet information. */
510 login = message->leftover;
511
512 /* Check the login name for validity. The login name is currently
513 is allowed to contain lowercase letters and numbers in any
514 position and underscore characters in any position but the
515 first. */
516 if ((strlen(login) < MIN_UNAME) || (strlen(login) > MAX_UNAME))
517 status = UREG_INVALID_UNAME;
518 }
519 if (status == SUCCESS)
520 if (login[1] == '_')
521 status = UREG_INVALID_UNAME;
522 if (status == SUCCESS)
523 {
524 for (i = 0; i < strlen(login); i++)
525 if (!islower(login[i]) && !isdigit(login[i]) &&
526 (login[i] != '_'))
527 {
528 status = UREG_INVALID_UNAME;
529 break;
530 }
531 }
532 if (status == SUCCESS)
533 {
534 /* Now that we have a valid user with a valid login... */
535
536 /* First, try to reserve the user in SMS. */
537 (void) sprintf(fstype_buf,"%d",SMS_FS_STUDENT);
538 q_name = "register_user";
539 q_argv[0] = message->db.uid;
540 q_argv[1] = login;
541 q_argv[2] = fstype_buf;
542 q_argc = 3;
543 status = sms_query(q_name,q_argc,q_argv,null_callproc,(char *)0);
544 switch (status)
545 {
546 case SMS_SUCCESS:
547 status = SUCCESS;
548 break;
549 case SMS_IN_USE:
550 status = UREG_LOGIN_USED;
551 break;
552 default:
553 status = UREG_MISC_ERROR;
554 critical_alert(FAIL_INST,"%s returned from register_user.",
555 error_message(status));
556 break;
557 }
558 }
559 if (status == SUCCESS)
560 {
561 /* SMS login was successfully created; try to reserve kerberos
562 principal. */
563 /* If this routine fails, store the login in the retval so
564 that it can be used in the client-side error message. */
565 if ((status = do_admin_call(login, "", message->db.uid)) != SUCCESS)
566 (void) strcpy(retval, login);
567 }
568
569 com_err(whoami, status, " returned from reserve_user");
570
571 return status;
572}
573
574int set_final_status(login)
575 char *login;
576 /* This routine updates a user's registration status to fully
577 registered. */
578{
579 char *q_name; /* Name of SMS query */
580 int q_argc; /* Number of arguments for SMS query */
581 char *q_argv[2]; /* Arguments to get user by uid */
582 char state[7]; /* Can hold a 16 bit integer */
583 int status; /* Error status */
584
585 com_err(whoami, 0, "Setting final status for %s", login);
586
587 (void) sprintf(state,"%d",US_REGISTERED);
588 q_name = "update_user_status";
589 q_argc = 2;
590 q_argv[0] = login;
591 q_argv[1] = state;
592 if ((status = sms_query(q_name, q_argc, q_argv, null_callproc,
593 (char *)0)) != SMS_SUCCESS)
594 critical_alert(FAIL_INST,"%s returned from update_user_status.",
595 error_message(status));
596
597 com_err(whoami,status," returned from set_final_status");
598 return status;
599}
600
601
602int set_password(message,retval)
603 struct msg *message;
604 char *retval;
605 /* This routine is used to set the initial password for the new user. */
606{
607 int status = SUCCESS; /* Return status */
608 char *passwd; /* User's password */
609
610 com_err(whoami, 0, " set_password %s %s",
611 message->first, message->last);
612
613 status = verify_user(message,retval);
614
615 /* Don't set the password unless the registration status of the user
616 is that he exists and has no password. */
617 if (status == SUCCESS)
618 status = UREG_NO_LOGIN_YET;
619 if (status == UREG_NO_PASSWD_YET)
620 {
621 /* User is in proper state for this transaction. */
622
623 passwd = message->leftover;
624
625 /* Set password. */
626 if ((status = do_admin_call(message->db.login,
627 passwd, message->db.uid)) != SUCCESS)
628 /* If failure, allow login name to be used in client
629 error message */
630 (void) strcpy(retval,message->db.login);
631 else
632 /* Otherwise, mark user as finished. */
633 status = set_final_status(message->db.login);
634 }
635 com_err(whoami, status, " returned from set_passwd");
636
637 return status;
638}
This page took 0.039003 seconds and 5 git commands to generate.