]> andersk Git - moira.git/blob - reg_svr/reg_svr.c
Initial revision
[moira.git] / reg_svr / reg_svr.c
1 /*
2  *      $Source$
3  *      $Author$
4  *      $Header$
5  *
6  *      Copyright (C) 1987 by the Massachusetts Institute of Technology
7  *
8  *      Server for user registration with SMS and Kerberos.
9  *
10  *      This program is a client of the Kerberos admin_server and a
11  *      server for the userreg program.  It is not a client of the
12  *      SMS server as it is linked with libsmsglue which bypasses
13  *      the network protocol.
14  */
15
16 #ifndef lint
17 static char *rcsid_reg_svr_c = "$Header$";
18 #endif lint
19
20 #include <stdio.h>
21 #include <strings.h>
22 #include <ctype.h>
23 #include <sys/types.h>
24 #include <sys/file.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
28 #include <krb.h>
29 #include <des.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include "ureg_err.h"
33 #include "ureg_proto.h"
34 #include "sms.h"
35 #include "sms_app.h"
36 #include "infodefs.h"
37 #include "admin_server.h"
38 #include "admin_err.h"
39
40 #ifndef TRUE
41 #define TRUE 1
42 #endif
43
44 #ifndef FALSE
45 #define FALSE 0
46 #endif
47
48 #define FAIL_INST "reg_svr"     /* Instance for failure zephyrgrams */
49
50 #define CUR_UREG_VERSION 1      /* Version for the register protocol */
51 #define SUCCESS 0               /* General purpose success code */
52 #define FAILURE 1               /* To use when any non-zero number will work */
53 #define min(a,b) ((a)>(b)?(b):(a))
54 #define MIN_UNAME 3             /* Username must be between three and */
55 #define MAX_UNAME 8             /*    eight characters long. */
56 #define CRYPT_LEN 14            /* crypt() returns a 13 char string */
57 #define LOGIN_LEN MAX_UNAME + 1 /* Leave room for a null */
58 #define UID_LEN 7               /* Allow room for a 16 bit number */
59
60 #define DEBUG
61 /* This MUST be correct!  There isn't a good way of getting this without
62    hardcoding that would not make testing impractical. */
63
64 extern char *strdup();
65 extern char *malloc();
66 extern int krb_err_base;
67 extern char admin_errmsg[];
68
69 extern char *whoami;            /* Name of program - used by libraries */
70 extern int errno;               /* Unix error number */
71
72 /* This structure holds information from the SMS database that will be
73    worth holding on to.  An instance of it appears in the formatted 
74    packet structure. */
75 struct db_data
76 {
77     char mit_id[CRYPT_LEN];     /* Encrypted MIT ID */
78     int reg_status;             /* Registration status */
79     char uid[UID_LEN];          /* Reserved uid */
80     char login[LOGIN_LEN];      /* Login (username) */
81 };
82
83 /* This structure stores information sent over in the packet in a 
84    more convenient format and also stores some information obtained 
85    from the database that will be needed for each transaction.  It
86    initialized from format_pkt() and find_user(). */
87 struct msg
88 {    
89     U_32BIT version;            /* User registration protocol version */
90     U_32BIT request;            /* Request */
91     char *first;                /* First name */
92     char *last;                 /* Last name */
93     char *encrypted;            /* Encrypted information in packet */
94     int encrypted_len;          /* Length of encrypted information in packet */
95     char *leftover;             /* Leftover information sent in the packet */
96     int leftover_len;           /* Length of leftover information */
97     struct db_data db;          /* Information from the SMS database */
98 };
99
100 static char krbhst[BUFSIZ];     /* kerberos server name */
101 static char krbrealm[REALM_SZ]; /* kerberos realm name */
102
103
104 main(argc,argv)
105   int argc;
106   char *argv[];
107 {
108     struct servent *sp;         /* Service info from /etc/services */
109     int s;                      /* Socket descriptor */
110     struct sockaddr_in sin;     /* Internet style socket address */
111     int addrlen;                /* Size of socket address (sin) */
112     char packet[BUFSIZ];        /* Buffer for packet transmission */
113     int pktlen;                 /* Length of packet */
114     U_32BIT seqno;              /* Sequence number for packet transmission */
115     struct msg message;         /* Storage for parsed packet */
116     int status = SUCCESS;       /* Error status */
117     char retval[BUFSIZ];        /* Buffer to hold return message for client */
118     
119     void failure_alert();       /* Log an unexplainable failure */
120     int parse_pkt();            /* Parse a packet from the client */
121     int format_pkt();           /* Prepare a packet to send to client*/
122     int verify_user();          /* Make sure user is allowed to register */
123     int reserve_user();         /* Reserve a login for this user */
124     int set_password();         /* Set this user's password */
125
126     whoami = argv[0];
127
128     /* Error messages sent one line at a time */
129     setlinebuf(stderr);
130     
131     /* Initialize user registration error table for com_err */
132     init_ureg_err_tbl();
133     
134     /* Get service information from /etc/services */
135     if ((sp = getservbyname("sms_ureg", "udp")) == NULL) 
136     {
137         com_err(whoami, errno, " unknown service sms_ureg/udp");
138         exit(1);
139     }
140     
141     /* Get an internet style datagram socket */
142     if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) 
143     {
144         com_err(whoami,errno," socket");
145         exit(1);
146     }
147     bzero((char *)&sin,(int)sizeof(sin));
148     
149     sin.sin_family = AF_INET;
150     sin.sin_port = sp->s_port;
151     sin.sin_addr.s_addr = INADDR_ANY;
152     
153     /* Bind a name to the socket */
154     if (bind(s, &sin, sizeof(sin)) < 0) 
155     {
156         com_err(whoami,errno," bind");
157         exit(1);
158     }
159     
160     /* Connect to the SMS server */
161     if ((status = sms_connect()) != SMS_SUCCESS) 
162     {
163         com_err(whoami, status, " on connect");
164         exit(1);
165     }
166     
167     /* Authorize, telling the server who you are */
168     if ((status = sms_auth(whoami)) != SMS_SUCCESS) 
169     {
170         com_err(whoami, status, " on auth");
171         exit(1);
172     }
173
174     if (status = get_krbrlm(krbrealm, 1)) {
175         status += krb_err_base;
176         com_err(whoami, status, " fetching kerberos realm");
177         exit(1);
178     }
179
180     if (status = get_krbhst(krbhst, krbrealm, 1)) {
181         status += krb_err_base;
182         com_err(whoami, status, " fetching kerberos hostname");
183         exit(1);
184     } else {
185         char *s;
186         for (s = krbhst; *s && *s != '.'; s++)
187             if (isupper(*s))
188                 *s = tolower(*s);
189         *s = 0;
190     }
191
192     /* Use com_err or output to stderr for all log messages. */    
193 #ifdef DEBUG
194     fprintf(stderr,"*** Debugging messages enabled. ***\n");
195 #endif DEBUG
196
197     /* Sit around waiting for requests from the client. */
198     for (;;) 
199     {
200         com_err(whoami, 0, "*** Ready for next request ***");
201         addrlen = sizeof(sin);
202         bzero(retval, BUFSIZ);
203         /* Receive a packet */
204         if ((pktlen = recvfrom(s,packet,sizeof(packet),0,&sin,&addrlen)) < 0) 
205         {
206             com_err(whoami,errno," recvfrom");
207             /* Don't worry if error is interrupted system call. */
208             if (errno == EINTR) continue;
209             exit(1);
210         }
211         
212         /* Parse a request packet */
213         if ((status = parse_pkt(packet, pktlen, &seqno, &message)) != SUCCESS) 
214         {
215             /* If error, format packet to send back to the client */
216             pktlen = sizeof(packet);
217             (void) format_pkt(packet, &pktlen, seqno, status, (char *)NULL);
218             /* Report the error the the client */
219             (void)  sendto(s, packet, pktlen, 0, &sin, addrlen);
220             continue;
221         }
222         
223         /* do action */
224         switch((int)message.request) 
225         {
226           case UREG_VERIFY_USER:
227             status = verify_user(&message,retval);
228             break;
229           case UREG_RESERVE_LOGIN:
230             status = reserve_user(&message,retval);
231             break;
232           case UREG_SET_PASSWORD:
233             status = set_password(&message,retval);
234             break;
235             
236           default:
237             status = UREG_UNKNOWN_REQUEST;
238             failure_alert("Unknown request %d from userreg.",
239                           message.request);
240             break;
241         }
242         
243         /* Report what happened to client */
244         pktlen = sizeof(packet);
245         if (format_pkt(packet, &pktlen, seqno, status, retval))
246         {
247             com_err(whoami,0,"Client error message was truncated.");
248         }
249         (void) sendto(s, packet, pktlen, 0, &sin, addrlen);
250     }
251 }
252
253 /* This is necessary so that this server can know where to put its
254    tickets. */
255 char *tkt_string()
256 {
257     return("/tmp/tkt_ureg");
258 }
259
260 void failure_alert(msg, args)
261   char *msg;                    /* Text of message, printf format */
262                                 /* args = arguments, printf style */
263   /* This routine takes care of logging critical error from userreg
264      in the appropriate way. */
265 {
266     critical_alert(FAIL_INST, msg, args);
267     com_err(whoami, 0, msg, args);
268 }
269
270 int format_pkt(packet, pktlenp, seqno, cl_status, message)
271   char *packet;                 /* Packet buffer */
272   int *pktlenp;                 /* Pointer to packet size */
273   U_32BIT seqno;                /* Sequence number */
274   int cl_status;                /* Error status to return to client */
275   char *message;                /* Error message to return to client */
276   /* This routine prepares a packet to send back to the client.  A 
277      non-zero return status means that the client error message was 
278      truncated. */
279 {
280     int len;                    /* Amount of message to send */
281     int status = SUCCESS;       /* Return status */
282
283     /* Convert byte order to network byte order */
284     U_32BIT vers = htonl((U_32BIT)CUR_UREG_VERSION);
285     cl_status = htonl((U_32BIT)cl_status);
286     /* Put current user registration protocol version into the packet */
287     bcopy((char *)&vers, packet, sizeof(U_32BIT));
288     /* Put sequence number into the packet */
289     bcopy((char *)&seqno, packet+sizeof(U_32BIT), sizeof(U_32BIT));
290     /* Put error status into the packet */
291     bcopy((char *)&cl_status, packet+ 2*sizeof(U_32BIT), sizeof(U_32BIT));
292     
293     /* Find out how much of the message to copy; truncate if too short. */
294     /* How much room is there left? */
295     len = *pktlenp - sizeof(U_32BIT)*3;
296     if (len < strlen(message) + 1) /* Room for null terminator */
297     {
298         status = FAILURE;       /* Message was truncated */
299         /* Truncate the message */
300         message[len-1] = NULL;
301     }
302
303     /* Copy the message into the packet */
304     (void) strcpy(packet+3*sizeof(U_32BIT), message);
305     *pktlenp = 3*sizeof(U_32BIT) + strlen(message);
306     
307     return status;
308 }
309
310 int parse_encrypted(message,data)
311   struct msg *message;          /* Formatted packet */
312   struct db_data *data;         /* Data from the SMS database */
313 /* This routine makes sure that the ID from the database matches
314    the ID sent accross in the packet.  The information in the packet
315    was created in the following way:
316
317    The plain text ID number was encrypted via EncryptID() resulting
318    in the form that would appear in the SMS database.  This is
319    concatinated to the plain text ID so that the ID string contains plain
320    text ID followed by a null followed by the encrypted ID.  Other
321    information such as the username or password is appended.  The whole
322    thing is then DES encrypted using the encrypted ID as the source of
323    the key.
324
325    This routine tries each encrypted ID in the database that belongs
326    to someone with this user's first and last name and tries to 
327    decrypt the packet with this information.  If it succeeds, it returns 
328    zero and initializes all the fields of the formatted packet structure
329    that depend on the encrypted information. */
330 {
331     C_Block key;                /* The key for DES en/decryption */
332     Key_schedule sched;         /* En/decryption schedule */
333     static char decrypt[BUFSIZ];   /* Buffer to hold decrypted information */
334     long decrypt_len;           /* Length of decypted ID information */
335     char recrypt[14];           /* Buffer to hold re-encrypted information */
336     static char hashid[14];     /* Buffer to hold one-way encrypted ID */
337     char idnumber[BUFSIZ];      /* Buffer to hold plain-text ID */
338     char *temp;                 /* A temporary string pointer */
339     int len;                    /* Keeps track of length left in packet */
340     int status = SUCCESS;       /* Error status */
341     
342 #ifdef DEBUG
343     com_err(whoami,0,"Entering parse_encrypted");
344 #endif
345
346     /* Make the decrypted information length the same as the encrypted
347        information length.  Both are integral multples of eight bytes 
348        because of the DES encryption routines. */
349     decrypt_len = (long)message->encrypted_len;
350     
351     /* Get key from the one-way encrypted ID in the SMS database */
352     string_to_key(data->mit_id, key);
353     /* Get schedule from key */
354     key_sched(key, sched);
355     /* Decrypt information from packet using this key.  Since decrypt_len
356        is an integral multiple of eight bytes, it will probably be null-
357        padded. */
358     pcbc_encrypt(message->encrypted,decrypt, decrypt_len, sched, key, DECRYPT);
359     
360     /* Extract the plain text and encrypted ID fields from the decrypted
361        packet information. */
362     /* Since the decrypted information starts with the plain-text ID
363        followed by a null, if the decryption worked, this will only 
364        copy the plain text part of the decrypted information.  It is
365        important that strncpy be used because if we are not using the
366        correct key, there is no guarantee that a null will occur
367        anywhere in the string. */
368     (void) strncpy(idnumber,decrypt,(int)decrypt_len);
369     /* Point temp to the end of the plain text ID number. */
370     temp = decrypt + strlen(idnumber) + 1;
371     /* Find out how much more packet there is. */
372     len = message->encrypted_len - (temp - decrypt);
373     /* Copy the next CRYPT_LEN bytes of the decrypted information into 
374        hashid if there are CRYPT_LEN more bytes to copy.  There will be
375        if we have the right key. */
376     (void) strncpy(hashid, temp, min(len, CRYPT_LEN));
377     /* Point temp to the end of the encrypted ID field */
378     temp += strlen(hashid) + 1;
379     /* Find out how much more room there is. */
380     len = message->encrypted_len - (temp - decrypt);
381     
382     /* Now compare encrypted ID's don't match. */
383     if (strcmp(hashid, data->mit_id)) status = FAILURE;
384     if (status == SUCCESS)
385     {
386         EncryptID(recrypt, idnumber, message->first, message->last);
387         /* Now compare encrypted plain text to ID from database. */
388         if (strcmp(recrypt, data->mit_id)) status = FAILURE;
389     }
390     
391     if (status == SUCCESS)
392     {
393         /* We made it.  Now we can finish initializing message. */
394         /* Point leftover to whatever is left over! */
395         message->leftover = temp;
396         message->leftover_len = len;
397         /* Since we know we have the right user, fill in the information 
398            from the SMS database. */
399         message->db.reg_status = data->reg_status;
400         (void) strncpy(message->db.uid,data->uid, sizeof(message->db.uid));
401         (void) strncpy(message->db.mit_id,data->mit_id, 
402                        sizeof(message->db.mit_id));
403         (void) strncpy(message->db.login,data->login, sizeof(message->db.login));
404     }
405     
406 #ifdef DEBUG
407     if (status)
408         com_err(whoami,status," parse_encrypted failed.");
409     else
410         com_err(whoami,status,"parse_encrypted succeeded.");
411 #endif
412
413     return status;
414 }
415
416 int db_callproc(argc,argv,queue)
417   int argc;                     /* Number of arguments returned by SMS */
418   char *argv[];                 /* Arguments returned by SMS */
419   struct save_queue *queue;     /* Queue to save information in */
420 /* This function is called by sms_query after each tuple found.  It is
421    used by find_user to cache information about each user found.  */
422 {
423     struct db_data *data;       /* Structure to store the information in */
424     int status = SUCCESS;       /* Error status */
425     
426 #ifdef DEBUG
427     com_err(whoami,0,"Entering db_callproc.");
428 #endif
429
430     if (argc != U_END)
431     {
432         failure_alert
433             ("Wrong number of arguments returned from get_user_by_name.");
434         status = SMS_ABORT;
435     }
436     else
437     {
438         /* extract the needed information from the results of the SMS query */
439         data = (struct db_data *)malloc(sizeof(struct db_data));
440         data->reg_status = atoi(argv[U_STATE]);
441         (void) strncpy(data->login,argv[U_NAME],sizeof(data->login));
442         (void) strncpy(data->mit_id,argv[U_MITID],sizeof(data->mit_id));
443         (void) strncpy(data->uid,argv[U_UID],sizeof(data->uid));
444 #ifdef DEBUG
445         fprintf(stderr,"Found in database:\n");
446         fprintf(stderr,"   Registration status: %d\n",data->reg_status);
447         fprintf(stderr,"   login: %s\n",data->login);
448         fprintf(stderr,"   MIT ID: %s\n",data->mit_id);
449         fprintf(stderr,"   uid: %s\n",data->uid);
450 #endif
451         sq_save_data(queue,data);
452     }
453
454     return status;
455 }
456     
457 int find_user(message)
458   struct msg *message;          /* Formatted packet structure */
459 /* This routine verifies that a user is allowed to register by finding
460    him/her in the SMS database.  It returns the status of the SMS
461    query that it calls. */
462 {
463 #define GUBN_ARGS 2             /* Arguements needed by get_user_by_name */
464     char *q_name;               /* Name of query */
465     int q_argc;                 /* Number of arguments for query */
466     char *q_argv[GUBN_ARGS];    /* Arguments to query */
467     int status = SUCCESS;       /* Query return status */
468
469     struct save_queue *queue;   /* Queue to hold SMS data */
470     struct db_data *data;       /* Structure for data for one tuple */
471     short verified = FALSE;     /* Have we verified the user? */
472
473     /* Zero the mit_id field in the formatted packet structure.  This
474        being zeroed means that no user was found. */
475     bzero(message->db.mit_id,sizeof(message->db.mit_id));
476     
477     if (status == SUCCESS)
478     {
479         /* Get ready to make an SMS query */
480         q_name = "get_user_by_name";
481         q_argc = GUBN_ARGS;     /* #defined in this routine */
482         q_argv[0] = message->first;
483         q_argv[1] = message->last;
484         
485         /* Create queue to hold information */
486         queue = sq_create();
487         
488         /* Do it */
489         status = sms_query(q_name,q_argc,q_argv,db_callproc,(char *)queue);
490         
491 #ifdef DEBUG
492         fprintf(stderr," %d returned by get_user_by_name\n",status);
493 #endif
494         
495         if (status == SMS_SUCCESS) 
496         {
497             /* Traverse the list, freeing data as we go.  If sq_get_data()
498                returns zero if there is no more data on the queue. */
499             while (sq_get_data(queue,&data))
500             {
501                 if (!verified)
502                     /* parse_encrypted returns zero on success */
503                     verified = (parse_encrypted(message,data) == SUCCESS);
504                 free((char *)data);
505             }
506         }
507
508         /* Destroy the queue */
509         sq_destroy(queue);
510     }
511     
512 #ifdef DEBUG
513     fprintf(stderr,"Returned from find_user\n");
514     fprintf(stderr,"   MIT ID: %s\n", message->db.mit_id);
515     fprintf(stderr,"   Registration status: %d\n",message->db.reg_status);
516     fprintf(stderr,"   uid: %s\n",message->db.uid);
517     fprintf(stderr,"   login: %s\n",message->db.login);
518     fprintf(stderr,"   Status from query: %d\n",status);
519 #endif DEBGUG
520
521     return status;
522 }
523
524 /* The ureg_validate_char variable and routine were taken verbatim 
525    out of server/qsupport.qc where they are called
526    validate_chars.  At some point, it may be desirable
527    to put this functionality in one place. */
528
529 /* ureg_validate_char: verify that there are no illegal characters in
530  * the string.  Legal characters are printing chars other than 
531  * ", *, ?, \, [ and ].
532  */
533 static int illegalchars[] = {
534     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
535     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
536     0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* SPACE - / */
537     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 0 - ? */
538     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */
539     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, /* P - _ */
540     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
541     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - ^? */
542     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
543     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
544     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
545     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
546     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
547     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
548     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
549     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
550 };
551
552 ureg_validate_char(s)
553 register char *s;
554 {
555     while (*s)
556       if (illegalchars[*s++])
557         return(FAILURE);
558     return(SUCCESS);
559 }
560
561 parse_pkt(packet, pktlen, seqnop, message)
562   char *packet;
563   int pktlen;
564   U_32BIT *seqnop;
565   struct msg *message;
566   /* This routine checks a packet and puts the information in it in
567      a structure if it is valid. */
568 {
569     int status = SUCCESS;       /* Error status */
570
571     com_err(whoami,0,"Packet received");
572
573     if (pktlen < sizeof(U_32BIT)) status = UREG_BROKEN_PACKET;
574     if (status == SUCCESS)
575     {
576         /* Extract the user registration protocol version from the packet */
577         bcopy(packet, (char *)&message->version, sizeof(long));
578         /* Convert byte order from network to host */
579         message->version = ntohl(message->version);
580         /* Verify version */
581         if (message->version != CUR_UREG_VERSION) 
582             status = UREG_WRONG_VERSION;
583     }
584
585     if (status == SUCCESS)
586     {
587         packet += sizeof(U_32BIT);
588         pktlen -= sizeof(U_32BIT);
589         
590         if (pktlen < sizeof(U_32BIT))
591             status = UREG_BROKEN_PACKET;
592     }
593
594     if (status == SUCCESS)
595     {
596         /* Extract the sequence number from the packet */
597         bcopy(packet, (char *)seqnop, sizeof(long));
598         
599         packet += sizeof(U_32BIT);
600         pktlen -= sizeof(U_32BIT);
601         
602         if (pktlen < sizeof(U_32BIT))
603             status = UREG_BROKEN_PACKET;
604     }
605
606     if (status == SUCCESS)
607     {
608         /* Extract the request from the packet */
609         bcopy(packet, (char *)(&message->request), sizeof(U_32BIT));
610         message->request = ntohl(message->request);
611         packet += sizeof(U_32BIT);
612         pktlen -= sizeof(U_32BIT);
613         
614         /* Make sure that the packet contains only valid characters up 
615            to the next null */
616         if (ureg_validate_char(packet) != SUCCESS)
617         {
618             com_err(whoami,0,"Packet contains invalid characters.");
619             status = UREG_USER_NOT_FOUND;
620         }
621         else
622         {
623             /* Extract first name from the packet */
624             message->first = packet;
625             
626             /* Scan forward until null appears in the packet or there
627                is no more packet! */
628             for (; *packet && pktlen > 0; --pktlen, ++packet) continue;
629             if (pktlen <= 0) 
630                 status = UREG_BROKEN_PACKET;
631         }
632     }
633     
634     if (status == SUCCESS)
635     {
636         /* Skip over the null */
637         packet++, pktlen--;
638         
639         if (ureg_validate_char(packet) != SUCCESS)
640         {
641             com_err(whoami,0,"Packet contains invalid characters.");
642             status = UREG_USER_NOT_FOUND;
643         }
644         else
645         {
646             /* Extract last name from the packet */
647             message->last = packet;
648             
649             for (; *packet && pktlen > 0; --pktlen, ++packet) continue;
650             if (pktlen <= 0)
651                 status = UREG_BROKEN_PACKET;
652         }
653     }
654
655     if (status == SUCCESS)
656     {
657         packet++, pktlen--;
658         
659         if (pktlen <= 0)
660             status = UREG_BROKEN_PACKET;
661     }
662
663     /* Extract encrypted information from packet */
664     message->encrypted = packet;
665     message->encrypted_len = pktlen;
666     
667     if (status == SUCCESS)
668     {
669 #ifdef DEBUG
670         com_err(whoami,status,"%s\n%s%d\n%s%d\n%s%s\n%s%s",
671                 "Packet parsed successfully.  Packet contains:",
672                 "   Protocol version: ",message->version,
673                 "   Request: ",message->request,
674                 "   First name: ",message->first,
675                 "   Last name: ",message->last);
676 #else /* DEBUG */
677         com_err(whoami,status,"Request %d for %s %s",message->request,
678                 message->first,message->last);
679 #endif DEBUG    
680     }
681     else
682         com_err(whoami,status," - parse packet failed.");
683
684     return status;
685 }
686
687 int verify_user(message,retval)
688   struct msg *message;
689   char *retval;
690   /* This routine determines whether a user is in the databse and returns
691      his state so that other routines can figure out whether he is the 
692      correct state for various transactions. */
693      
694 {
695     int status = SUCCESS;       /* Return status */
696
697     /* Log that we are about to veryify user */
698     com_err(whoami,0,"verify_user %s %s",message->first,message->last);
699
700     /* Figure out what user (if any) can be found based on the
701        encrypted information in the packet.  (See the comment on 
702        parse_encrypted().) */
703
704     status = find_user(message);
705
706     /* If SMS coudn't find the user */
707     if (status == SMS_NO_MATCH) 
708         status = UREG_USER_NOT_FOUND;
709     else if (status == SMS_SUCCESS)
710     {
711         /* If the information sent over in the packet did not point to a
712            valid user, the mit_id field in the formatted packet structure
713            will be empty. */
714         if (message->db.mit_id[0] == NULL)
715             status = UREG_USER_NOT_FOUND;
716         /* If the user was found but the registration has already started,
717            use this as the status */
718         else
719         {
720             switch (message->db.reg_status)
721             {
722               case US_NO_LOGIN_YET:
723                 status = SUCCESS;
724                 break;
725               case US_REGISTERED:
726                 status = UREG_ALREADY_REGISTERED;
727                 break;
728               case US_NO_PASSWD:
729                 status = UREG_NO_PASSWD_YET;
730                 break;
731               case US_DELETED:
732                 status = UREG_DELETED;
733                 break;
734               case US_NOT_ALLOWED:
735                 status = UREG_NOT_ALLOWED;
736                 break;
737
738               default:
739                 status = UREG_MISC_ERROR;
740                 failure_alert("Bad user state for %s %s.",
741                               message->first,message->last);
742                 break;
743             }
744             /* Set retval to the login name so that the client can use
745                it in the error message it will give the user. */
746             (void) strcpy(retval,message->db.login);
747         }
748     }
749     
750     com_err(whoami,status," returned from verify_user");
751
752     return status;
753 }
754         
755 int ureg_get_tkt()
756 {
757     int status = SUCCESS;       /* Return status */
758
759     /* Get keys for interacting with Kerberos admin server. */
760     /* principal, instance, realm, service, service instance, life, file */
761     if (status = get_svc_in_tkt("register", "sms", krbrealm, "changepw", 
762                                 krbhst, 1, KEYFILE))
763         status += krb_err_base;
764
765 #ifdef DEBUG
766     if (status == SUCCESS)
767         com_err(whoami,status,"Succeeded in getting tickets.");
768     else
769         com_err(whoami,status,"Failed to get tickets.");
770 #endif
771     return status;
772 }
773
774 int null_callproc(argc,argv,message)
775   int argc;
776   char *argv[];
777   char *message;
778   /* This routine is a null callback that should be used for queries that
779      do not return tuples.  If it ever gets called, something is wrong. */
780 {
781     failure_alert("Something returned from an update query.");
782     return FAILURE;
783 }
784
785 int do_admin_call(login, passwd, uid)
786   char *login;                  /* Requested kerberos principal */
787   char *passwd;                 /* Requested password */
788   char *uid;                    /* Uid of user who owns this principal */
789   /* This routine gets tickets, makes the appropriate call to admin_call,
790      and destroys tickets. */
791 {
792     int status;                 /* Error status */
793     char uid_buf[20];           /* Holds uid for kerberos */
794
795     com_err(whoami,0,"Entering do_admin_call");
796
797     if ((status = ureg_get_tkt()) == SUCCESS)
798     {
799         /* Try to reserve kerberos principal.  To do this, send a 
800            password request and a null password.  It will only succeed
801            if there is no principal or the principal exists and has no 
802            password. */
803         /* 13 chars of placebo for backwards-compatability - the admin
804            server protocol reqires this. */
805         bzero(uid_buf,sizeof(uid_buf));
806         (void) sprintf(uid_buf, "%13s", uid);
807         
808         if ((status = admin_call(ADMIN_ADD_NEW_KEY_ATTR, login, 
809                                  "", passwd, uid_buf)) != KSUCCESS)
810         {
811             com_err(whoami,status," server error: %s",admin_errmsg);
812             
813             if (strcmp(admin_errmsg,
814                        "Principal already in kerberos database.") == 0)
815                 status = UREG_KRB_TAKEN;
816             failure_alert("%s is known to Kerberos but not SMS.", login);
817         }
818     }
819     
820     dest_tkt();
821     com_err(whoami,status," returned from do_adin_call");
822     return status;
823 }
824
825 int reserve_user(message,retval)
826   struct msg *message;
827   char *retval;
828 {
829     int q_argc;                 /* Number of arguments to query */
830     char *q_argv[3];            /* Arguments to SMS query */
831     char *q_name;               /* Name of SMS query */
832     int status = SUCCESS;       /* General purpose error status */
833     char fstype_buf[7];         /* Buffer to hold fs_type, a 16 bit number */
834     char *login;                /* The login name the user wants */
835     register int i;             /* A counter */
836
837     /* Log that we are about to reserve a user. */
838     com_err(whoami, 0, "reserve_user %s %s", 
839             message->first, message->last);
840     
841     /* Check to make sure that we can verify this user. */
842     if ((status = verify_user(message,retval)) == SUCCESS)
843     {
844         /* Get the requested login name from leftover packet information. */
845         login = message->leftover;
846
847         /* Check the login name for validity.  The login name is currently
848            is allowed to contain lowercase letters and numbers in any
849            position and underscore characters and periods in any position
850            but the first. */
851         if ((strlen(login) < MIN_UNAME) || (strlen(login) > MAX_UNAME))
852             status = UREG_INVALID_UNAME;
853     }
854     if (status == SUCCESS)
855         if ((login[0] == '.') || (login[1] == '_'))
856             status = UREG_INVALID_UNAME;
857     if (status == SUCCESS)
858     {
859         for (i = 0; i < strlen(login); i++)
860             if (!islower(login[i]) && !isdigit(login[i]) &&
861                 (login[i] != '_') && (login[i] != '.'))
862             {
863                 status = UREG_INVALID_UNAME;
864                 break;
865             }
866     }
867     if (status == SUCCESS)
868     {
869         /* Now that we have a valid user with a valid login... */
870
871         /* First, try to reserve the user in SMS. */
872         (void) sprintf(fstype_buf,"%d",SMS_FS_STUDENT);
873         q_name = "register_user";
874         q_argv[0] = message->db.uid;
875         q_argv[1] = login;
876         q_argv[2] = fstype_buf;
877         q_argc = 3;
878         status = sms_query(q_name,q_argc,q_argv,null_callproc,(char *)0);
879         switch (status)
880         {
881           case SMS_SUCCESS:
882             status = SUCCESS;
883             break;
884           case SMS_IN_USE:
885             status = UREG_LOGIN_USED;
886             break;
887           default:
888             status = UREG_MISC_ERROR;
889             failure_alert("%s returned from register_user.",
890                           error_message(status));
891             break;
892         }
893     }
894     if (status == SUCCESS)
895     {
896         /* SMS login was successfully created; try to reserve kerberos
897            principal. */
898         /* If this routine fails, store the login in the retval so
899            that it can be used in the client-side error message. */
900         if ((status = do_admin_call(login, "", message->db.uid)) != SUCCESS)
901             (void) strcpy(retval, login);
902     }
903
904     com_err(whoami, status, " returned from reserve_user");
905     
906     return status;
907 }
908
909 int set_final_status(login)
910   char *login;
911     /* This routine updates a user's registration status to fully 
912        registered. */
913 {
914     char *q_name;               /* Name of SMS query */
915     int q_argc;                 /* Number of arguments for SMS query */
916     char *q_argv[2];            /* Arguments to get user by uid */
917     char state[7];              /* Can hold a 16 bit integer */
918     int status;                 /* Error status */
919
920     com_err(whoami, 0, "Setting final status for %s", login);
921
922     (void) sprintf(state,"%d",US_REGISTERED);
923     q_name = "update_user_status";
924     q_argc = 2;
925     q_argv[0] = login;
926     q_argv[1] = state;
927     if ((status = sms_query(q_name, q_argc, q_argv, null_callproc,
928                             (char *)0)) != SMS_SUCCESS)
929         failure_alert("%s returned from update_user_status.",
930                       error_message(status));
931     
932     com_err(whoami,status," returned from set_final_status");
933     return status;
934 }
935
936
937 int set_password(message,retval)
938   struct msg *message;
939   char *retval;
940   /* This routine is used to set the initial password for the new user. */
941 {
942     int status = SUCCESS;       /* Return status */
943     char *passwd;               /* User's password */
944
945     com_err(whoami, 0, " set_password %s %s",
946             message->first, message->last);
947
948     status = verify_user(message,retval);
949
950     /* Don't set the password unless the registration status of the user
951        is that he exists and has no password. */
952     if (status == SUCCESS)
953         status = UREG_NO_LOGIN_YET;
954     if (status == UREG_NO_PASSWD_YET)
955     {
956         /* User is in proper state for this transaction. */
957         
958         passwd = message->leftover;
959         
960         /* Set password. */
961         if ((status = do_admin_call(message->db.login, 
962                                     passwd, message->db.uid)) != SUCCESS)
963             /* If failure, allow login name to be used in client 
964                error message */
965             (void) strcpy(retval,message->db.login);
966         else
967             /* Otherwise, mark user as finished. */
968             status = set_final_status(message->db.login);
969     }
970     com_err(whoami, status, " returned from set_passwd");
971     
972     return status;
973 }
974     
975 /*
976  * Local Variables:
977  * mode: c
978  * c-argdecl-indent: 2
979  * c-brace-offset: -4
980  * c-continued-statement-offset: 4
981  * c-indent-level: 4
982  * c-label-offset: -2
983  * End:
984  */
This page took 0.184572 seconds and 5 git commands to generate.