char *whoami, *hostname, *shorthostname;
char *find_usernames(char *first, char *middle, char *last);
+int check_username_available(char *username);
void fixname(char *name);
int register_user(int uid, char *username);
void mr_com_err(const char *whoami, long code, const char *fmt, va_list pvar);
char login[USERS_LOGIN_SIZE], first[USERS_FIRST_SIZE];
char middle[USERS_MIDDLE_SIZE], last[USERS_LAST_SIZE];
char fullname[USERS_FIRST_SIZE + USERS_MIDDLE_SIZE + USERS_LAST_SIZE];
- char class[USERS_TYPE_SIZE];
- int uid, status, secure, sqlstatus;
+ char class[USERS_TYPE_SIZE], pin[USERS_PIN_SIZE];
+ int uid, status, secure, sqlstatus, count;
EXEC SQL END DECLARE SECTION;
if (rc->uid || argc != 4)
ulast = argv[2];
id = argv[3];
+ EXEC SQL SELECT count(login) INTO :count FROM users WHERE clearid = :id;
+
/* "ORDER BY status" so that if there's both a matching state 0 entry
and a matching state 3 entry, we'll get the former. */
EXEC SQL DECLARE csr_id CURSOR FOR
- SELECT login, unix_uid, status, secure, first, middle, last, type
+ SELECT login, unix_uid, status, secure, pin, first, middle, last, type
FROM users WHERE clearid = :id ORDER BY status;
EXEC SQL OPEN csr_id;
while (1)
{
EXEC SQL FETCH csr_id INTO :login, :uid, :status,
- :secure, :first, :middle, :last, :class;
+ :secure, :pin, :first, :middle, :last, :class;
if (sqlca.sqlcode)
break;
strtrim(login);
strtrim(middle);
strtrim(last);
strtrim(class);
+ strtrim(pin);
+
+ /* It's possible they have both a deleted account and a status 8
+ * account. We can't compensate for that in the ORDER BY clause
+ * above, so check here. If they have more than one entry and the
+ * first one we get is deleted, skip it.
+ */
+ if (status == US_DELETED && count > 1)
+ continue;
/* Check names, allowing for the possibility that Moira and the
user might have them split up differently. eg, Mary/Ann/Singleton
continue;
if (strlen(first) > 3 && strlen(ufirst) < 3)
continue;
+ if (!*ufirst && !*ulast)
+ continue;
+
/* Ignore the middle name since Moira doesn't have those reliably */
break;
}
case US_REGISTERED:
case US_ENROLLED:
case US_ENROLL_NOT_ALLOWED:
+ case US_REGISTERED_KERBEROS_ONLY:
reply(rc, ALREADY_REGISTERED, "INIT", "c", NULL, login);
return;
break;
}
+ rc->user_status = status;
rc->uid = uid;
sprintf(fullname, "%s %s%s%s", first, middle, *middle ? " " : "", last);
if (!strcmp(class, "MITS"))
}
if (rc->id)
- reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
+ {
+ if (*pin != '\0')
+ reply(rc, FOUND, "GETI", "c", NULL, fullname, class);
+ else
+ reply(rc, FOUND, "GETW", "c", NULL, fullname, class);
+ }
else if (!rc->username)
reply(rc, FOUND, "GETL", "c", rc->suggestions, fullname, class);
else
{
- if (status == US_NO_LOGIN_YET)
+ if (rc->user_status == US_NO_LOGIN_YET ||
+ rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
{
status = check_kerberos(login);
- if (status == MR_SUCCESS)
+ if (status == MR_SUCCESS &&
+ rc->user_status != US_NO_LOGIN_YET_KERBEROS_ONLY)
status = register_user(rc->uid, login);
if (status == MR_IN_USE)
{
reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
}
+void SPIN(reg_client *rc, int argc, char **argv)
+{
+ EXEC SQL BEGIN DECLARE SECTION;
+ char pin[USERS_PIN_SIZE];
+ EXEC SQL END DECLARE SECTION;
+
+ if (!rc->id || argc != 1)
+ {
+ reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
+ return;
+ }
+
+ EXEC SQL SELECT pin INTO :pin FROM users WHERE clearid = :rc->id
+ AND status = :rc->user_status;
+ strtrim(pin);
+ if (strcmp(argv[0], pin) != 0)
+ {
+ reply(rc, BAD_PIN, "GETI", "d", NULL);
+ return;
+ }
+
+ free(rc->id);
+ rc->id = NULL;
+ if (!rc->username)
+ reply(rc, NO_MESSAGE, "GETL", "c", rc->suggestions);
+ else
+ {
+ register_user(rc->uid, rc->username);
+ reply(rc, FORCED_USERNAME, "GETP", "c", NULL, rc->username);
+ }
+}
+
+void CLGN(reg_client *rc, int argc, char **argv)
+{
+ int i;
+ char *login;
+ long status;
+
+ if (!rc->uid || rc->id || rc->username || argc != 1)
+ {
+ reply(rc, PROTOCOL_ERROR, "INIT", "c", NULL);
+ return;
+ }
+
+ login = argv[0];
+
+ /* make sure someone's not trying to overrun reply */
+ if (strlen(login) > 100)
+ {
+ com_err(whoami, 0, "Buffer overrun attempted? Closing connection");
+ rc->lastmod = 0;
+ return;
+ }
+
+ if ((strlen(login) < 3) || (strlen(login) > USERS_LOGIN_SIZE - 1) ||
+ (login[0] == '_') || isdigit(login[0]))
+ {
+ reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
+ 3, USERS_LOGIN_SIZE - 1);
+ return;
+ }
+
+ for (i = 0; i < strlen(login); i++)
+ {
+ if (!islower(login[i]) && !isdigit(login[i]) && (login[i] != '_'))
+ {
+ reply(rc, BAD_USERNAME, "GETL", "c", rc->suggestions, login,
+ 3, USERS_LOGIN_SIZE - 1);
+ return;
+ }
+ }
+
+ status = check_kerberos(login);
+ if (status == MR_SUCCESS)
+ {
+ status = check_username_available(login);
+ if (status == MR_SUCCESS)
+ {
+ reply(rc, USERNAME_AVAILABLE, "LOGC", "c", login, login);
+ return;
+ }
+ }
+
+ if (status == MR_IN_USE)
+ {
+ if (rc->reserved_username)
+ {
+ reply(rc, RESERVED_USERNAME_UNAVAILABLE, "INIT", "c", NULL,
+ rc->username);
+ return;
+ }
+ reply(rc, USERNAME_UNAVAILABLE, "GETL", "c", rc->suggestions);
+ return;
+ }
+ else if (status == MR_DOWN)
+ {
+ reply(rc, DATABASE_CLOSED, "INIT", "c", NULL);
+ return;
+ }
+ else if (status != MR_SUCCESS)
+ {
+ reply(rc, INTERNAL_ERROR, "INIT", "c", NULL, error_message(status));
+ return;
+ }
+}
+
void LOGN(reg_client *rc, int argc, char **argv)
{
int i;
status = check_kerberos(login);
if (status == MR_SUCCESS)
- status = register_user(rc->uid, login);
-
+ {
+ if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
+ EXEC SQL UPDATE users SET login = :login WHERE unix_uid = :rc->uid;
+ else
+ status = register_user(rc->uid, login);
+ }
if (status == MR_IN_USE)
{
if (rc->reserved_username)
reply(rc, KADM_ERROR, "INIT", "c", NULL, error_message(status));
return;
}
- EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
+
+ if (rc->user_status == US_NO_LOGIN_YET_KERBEROS_ONLY)
+ EXEC SQL UPDATE users SET status = 9 WHERE login = :login;
+ else
+ EXEC SQL UPDATE users SET status = 1 WHERE login = :login;
EXEC SQL COMMIT;
reply(rc, DONE, "INIT", "c", NULL, rc->username);
/* Register a user in Moira */
int register_user(int uid, char *username)
{
+ EXEC SQL BEGIN DECLARE SECTION;
+ char pin[USERS_PIN_SIZE];
+ EXEC SQL END DECLARE SECTION;
char uidbuf[10], *qargv[3], *motd = NULL;
long status;
status = krb_get_svc_in_tkt(REG_SVR_PRINCIPAL, REG_SVR_INSTANCE,
krb_realmofhost(hostname), MOIRA_SNAME,
- shorthostname, 1, KEYFILE);
+ shorthostname, 3, KEYFILE);
if (status)
status += ERROR_TABLE_BASE_krb;
else
- status = mr_auth("reg_svr");
+ status = mr_krb5_auth("reg_svr");
if (status)
{
com_err(whoami, status, "authenticating to moira");
return MR_INTERNAL;
}
+ EXEC SQL SELECT pin INTO :pin FROM users WHERE unix_uid = :uid;
+
sprintf(uidbuf, "%d", uid);
qargv[0] = uidbuf;
qargv[1] = username;
- qargv[2] = "IMAP";
+
+ /* HACK: If user has a PIN set, they're from Sloan.
+ * Give them Exchange poboxes.
+ */
+ if (*pin != '\0')
+ qargv[2] = "EXCHANGE";
+ else
+ qargv[2] = "IMAP";
+
status = mr_query("register_user", 3, qargv, NULL, NULL);
mr_disconnect();
return status;
return unames;
}
+/* This does the database-side checks to make sure a username is
+ * available.
+ */
+int check_username_available(char *username)
+{
+ int count;
+
+ EXEC SQL SELECT COUNT(login) INTO :count FROM users
+ WHERE login = :username;
+ if (sqlca.sqlcode)
+ return MR_DBMS_ERR;
+ if (count != 0)
+ return MR_IN_USE;
+
+ EXEC SQL SELECT COUNT(name) INTO :count FROM list
+ WHERE name = :username;
+ if (sqlca.sqlcode)
+ return MR_DBMS_ERR;
+ if (count != 0)
+ return MR_IN_USE;
+
+ EXEC SQL SELECT COUNT(label) INTO :count FROM filesys
+ WHERE label = :username;
+ if (sqlca.sqlcode)
+ return MR_DBMS_ERR;
+ if (count != 0)
+ return MR_IN_USE;
+
+ return MR_SUCCESS;
+}
+
void fixname(char *name)
{
char *s, *d;